2011-11-04 5 views
35

Poniżej mówi się, że lepiej jest mieć pierwszych/drugich członków publicznych. Uważam, że to prawie tak źle. Jeśli dajesz sposób na dostęp do prywatnej zmiennej poza klasą, to jaki jest sens? Nie powinny być funkcjeCzy zwróciło się odwołanie do złych praktyk zmiennych zmiennych?

T First(); void(or T) First(const T&) 

Próbka:

// Example 17-3(b): Proper encapsulation, initially with inline accessors. Later 
// in life, these might grow into nontrivial functions if needed; if not, then not. 
// 
template<class T, class U> 
class Couple { 
    Couple()   : deleted_(false) { } 
    T& First()   { return first_; } 
    U& Second()  { return second_; } 
    void MarkDeleted() { deleted_ = true; } 
    bool IsDeleted() { return deleted_; } 

private: 
T first_; 
U second_; 
bool deleted_; 
}; 

Odpowiedz

43

Istnieje kilka powodów, dla których zwracanie referencji (lub wskaźników) do elementów wewnętrznych klasy jest złe. Wychodząc z (co uważam za) najważniejsze:

  1. Encapsulation została naruszona: ty wyciek szczegółów wdrażania, co oznacza, że ​​nie można już zmieniać swoje wewnętrzne klasy, jak chcesz. Jeśli zdecydujesz się nie przechowywać na przykład first_, ale aby obliczyć go w locie, w jaki sposób zwrócisz do niego odniesienie? Nie możesz, więc utknąłeś.

  2. Inwariant nie są już trwały (w przypadku odniesienia const): ktoś może uzyskać dostęp i zmodyfikować atrybut, o których mowa w woli, a więc nie można „kontrolować” do jego zmiany. Oznacza to, że nie można zachować niezmiennika, którego ten atrybut jest częścią. Zasadniczo twoja klasa zamienia się w kropelkę.

  3. Dożywotnia pojawia się kwestia: łatwo jest zachować referencję lub wskaźnik do atrybutu po tym, jak oryginalny obiekt, do którego należy, przestał istnieć. Jest to oczywiście niezdefiniowane zachowanie. Większość kompilatorów będzie próbowała ostrzegać o zachowywaniu odniesień do obiektów na stosie, ale nie znam żadnego kompilatora, który zdołałby wygenerować takie ostrzeżenia dla referencji zwróconych przez funkcje lub metody: jesteś sam.

Zwykle lepiej jest nie podawać referencji lub wskaźników atrybutom. Nawet nie jeden const!

Dla małych wartości zwykle wystarcza przekazanie ich za pomocą kopii (zarówno in, jak i out), szczególnie teraz z semantyką ruchu (po drodze).

W przypadku większych wartości, to naprawdę zależy od sytuacji, czasami Proxy może złagodzić Twoje problemy.

Wreszcie, należy pamiętać, że dla niektórych klas posiadanie członków publicznych nie jest takie złe.Jaki byłby cel hermetyzacji członków pair? Kiedy piszesz klasę, która jest niczym więcej niż zbiorem atrybutów (bez inwariantu), zamiast zamiast tego otrzymywać wszystkie OO i pisać parę getter/setter dla każdego z nich, zastanów się nad ich publicznym udostępnieniem.

+2

+1, ale istnieje kontrproporcja dla zapewnienia dostępu: można dodać oprzyrządowanie do wywołania, co ułatwia znajdowanie miejsc, w których obiekt jest aktualizowany w kodzie, lub dołączanie debuggera w celu wykrycia problemów. –

+1

Dla ciebie informacji w przykładzie mówiono o klasach, które mają prywatnych członków stąd 'bool deleted_;'. Lubię POD –

+0

@David: Nie jestem przeciwnikiem dostępu, jak w 'T get()/void set (T)', w którym możesz skutecznie śledzić zmiany swoich wartości, zapewniać leniwą kalkulację i wiele innych rzeczy, ale Jedynym ciekawym ciekawostką w 'T & access()' jest "śledzenie", że uzyskano dostęp, który nie przynosi wiele do stołu. –

17

Jeśli template rodzaje T i U są duże konstrukcje następnie powrót wartością jest kosztowne. Jednakże masz rację, że zwracanie przez odniesienie jest równoważne z przyznaniem dostępu do zmiennej private. Aby rozwiązać oba problemy, uczynić je const referencje:

const T& First() const { return first_; } 
const U& Second() const { return second_; } 

PS: Nieprawidłowe zachowanie zmiennych wewnątrz konstruktora jest również złe, gdy nie ma metody ustawiającej. Wygląda na to, że w oryginalnym kodzie, First() i Second() są owijkami ponad first_ i second_, które były przeznaczone do odczytu i zapisu.

+0

ah ha, dobre rozwiązanie. Dopóki odczyty ('First()') ma takie efekty uboczne to byłoby całkowicie w porządku (nie jest to nieuzasadnione ograniczenie, ale wciąż ograniczenie :)) –

+2

+1. Nice ........ – Nawaz

+3

[Ten link] (http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/) ma kilka interesujących wglądów w przekazywanie wartości i wartości R. – juanchopanza

6

Odpowiedź zależy od tego, co próbuje się zrobić. Powracające odwołania są wygodnym sposobem na ułatwienie mutacji struktur danych. Dobrym przykładem jest mapa STL. Zwraca referencję do elementu tj

std::map<int,std::string> a; 
a[1] = 1; 

nic nie powstrzyma cię od robienia

auto & aref = a[1]; 

Czy koniecznie złą praktyką? Nie sądzę. Powiedziałbym, że jeśli możesz zrobić to bez niego, zrób to. Jeśli to sprawia, że ​​życie jest bardziej wygodne i efektywne, użyj go i bądź świadomy tego, co robisz.

Powiązane problemy