Czy powinienem preferować wskaźniki lub odniesienia w danych członkowskich?

Jest to uproszczony przykład do zilustrowania pytania:

class A {};

class B
{
    B(A& a) : a(a) {}
    A& a;
};

class C
{
    C() : b(a) {} 
    A a;
    B b; 
};

Więc B jest odpowiedzialny za aktualizację części C. przepuściłem Kod przez lint i skomlał o członie odniesienia: lint#1725 . Mówi to o dbaniu o domyślną kopię i przypisanie, co jest wystarczająco uczciwe, ale domyślna kopia i przypisanie są również złe ze wskaźnikami, więc nie ma tam małej przewagi.

Zawsze staram się używać referencji tam, gdzie mogę, ponieważ nagie wskaźniki wprowadzają niepewnie, kto jest odpowiedzialny za usunięcie tego wskaźnika. Wolę osadzać obiekty według wartości, ale jeśli potrzebuję wskaźnika, używam auto_ptr w danych członkowskich klasy, która jest właścicielem wskaźnika, i przekazuję obiekt jako odniesienie.

Zazwyczaj używałbym wskaźnika w danych członkowskich tylko wtedy, gdy wskaźnik może być null lub może się zmienić. Czy istnieją inne powody, aby preferować wskaźniki niż odniesienia dla członków danych?

Czy prawdą jest, że obiekt zawierający odniesienie nie powinno być przypisane, ponieważ odniesienie nie powinno być zmieniane po zainicjowaniu?

Author: TemplateRex, 2009-05-21

9 answers

Unikaj członków referencyjnych, ponieważ ograniczają to, co może zrobić Implementacja klasy (w tym, jak wspomniałeś, uniemożliwiając implementację operatora przypisania) i nie zapewniają żadnych korzyści temu, co klasa może zapewnić.

Przykładowe problemy:

  • jesteś zmuszony zainicjalizować referencję w liście inicjalizatorów każdego konstruktora: nie ma sposobu, aby włączyć tę inicjalizację do innej funkcji ( dopóki C++0x, tak czy inaczej edit: C++ ma teraz delegowanie konstruktorów )
  • odniesienie nie może być rebound lub null. Może to być zaletą, ale jeśli kod kiedykolwiek wymaga zmiany, aby umożliwić ponowne łączenie lub aby członek był null, wszystkie zastosowania członka muszą zmienić
  • W przeciwieństwie do elementów wskaźnikowych, odniesienia nie mogą być łatwo zastąpione inteligentnymi wskaźnikami lub iteratorami, ponieważ refaktoryzacja może wymagać
  • Kiedy Referencja jest używana, wygląda jak typ wartości (. operator itp.), ale zachowuje się jak wskaźnik (może zwisać) - czyli np. Google Style Guide]}
 39
Author: James Hopkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-21 13:51:38

Moja własna zasada:

  • Użyj elementu referencyjnego, gdy chcesz, aby życie Twojego obiektu było zależne od życia innych obiektów: jest to jednoznaczny sposób, aby powiedzieć, że nie pozwalasz obiektowi żyć bez poprawnej instancji innej klasy - z powodu braku przypisania i obowiązku uzyskania inicjalizacji referencji przez konstruktor. to dobry sposób na zaprojektowanie klasy bez zakładania, że jej instancja jest członkiem lub nie kolejna klasa.Zakładasz tylko, że ich życie jest bezpośrednio powiązane z innymi instancjami. Pozwala na późniejszą zmianę sposobu korzystania z instancji klasy (z new, jako lokalna instancja, jako członek klasy, generowana przez pulę pamięci w Menedżerze itp.)
  • użyj wskaźnika w innych przypadkach: Jeśli chcesz, aby członek został zmieniony później, użyj wskaźnika lub wskaźnika const, aby mieć pewność, że tylko odczyta wskazywaną instancję. Jeśli ten typ ma być kopiowalny, nie można użyć w każdym razie referencje. czasami musisz również zainicjować element po specjalnym wywołaniu funkcji (na przykład init ()) i wtedy po prostu nie masz wyboru, jak tylko użyć wskaźnika. ale: użyj twierdzeń we wszystkich funkcjach członkowskich, aby szybko wykryć zły stan wskaźnika!
  • w przypadkach, gdy chcesz, aby żywotność obiektu była zależna od żywotności obiektu zewnętrznego i musisz również, aby ten typ był kopiowalny, użyj elementów wskaźnikowych, ale argumentu referencyjnego w konstruktor w ten sposób wskazujesz na konstrukcji, że żywotność tego obiektu zależy od żywotności argumentu, ale implementacja używa wskaźników, aby nadal można było kopiować. Tak długo, jak te elementy są zmieniane tylko przez kopiowanie, a Twój typ nie ma domyślnego konstruktora, Typ powinien wypełnić oba cele.
 117
Author: Klaim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-28 22:55:46

Obiekty rzadko powinny pozwalać na przypisywanie i inne rzeczy, takie jak porównanie. Jeśli weźmiemy pod uwagę jakiś model biznesowy z obiektami takimi jak "dział", "pracownik", "dyrektor", trudno sobie wyobrazić przypadek, gdy jeden pracownik zostanie przypisany do innego.

Tak więc w przypadku obiektów biznesowych bardzo dobrze jest opisać relacje jeden do jednego i jeden do wielu jako odniesienia, a nie wskaźniki.

I prawdopodobnie dobrze jest opisać relację jedno-lub-zero jako wskaźnik.

Więc nie "nie możemy przypisać" więc czynnik.
Wielu programistów po prostu przyzwyczaja się do wskaźników i dlatego znajdą jakikolwiek argument, aby uniknąć użycia referencji.

Posiadanie wskaźnika jako członka zmusi Ciebie lub członka Twojego zespołu do wielokrotnego sprawdzania wskaźnika przed użyciem, z komentarzem "na wszelki wypadek". Jeśli wskaźnik może być zerowy, wskaźnik prawdopodobnie jest używany jako rodzaj flagi, co jest złe, ponieważ każdy obiekt musi odgrywać swoją rolę.

 32
Author: Mykola Golubyev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-21 10:10:10
 11
Author: ebo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-21 10:00:38

W kilku ważnych przypadkach przypisanie nie jest po prostu potrzebne. Są to często lekkie owijarki algorytmów, które ułatwiają obliczenia bez opuszczania zakresu. Takie obiekty są głównymi kandydatami na członków referencyjnych, ponieważ możesz być pewien, że zawsze zawierają Ważne odniesienie i nigdy nie muszą być kopiowane.

W takich przypadkach upewnij się, że operator przypisania (a często także Konstruktor kopiujący) nie jest użyteczny (dziedzicząc z boost::noncopyable lub deklarując je prywatne).

Jednak, jak już skomentował użytkownik pts, to samo nie dotyczy większości innych obiektów. W tym przypadku Używanie elementów referencyjnych może być ogromnym problemem i ogólnie należy ich unikać.

 6
Author: Konrad Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-21 09:55:00

Jako, że wszyscy wydają się rozdawać ogólne zasady, proponuję dwa:

  • Nigdy, przenigdy nie używaj referencji use jako członków klasy. Nigdy nie zrobiłem tego w moim własnym kodzie (z wyjątkiem udowodnienia sobie, że miałem rację w tej zasadzie) i nie wyobrażam sobie przypadku, w którym bym to zrobił. Semantyka jest zbyt myląca i naprawdę nie do tego zostały zaprojektowane odniesienia.

  • Zawsze, Zawsze, używaj referencji przy przekazywaniu parametrów do funkcji, z wyjątkiem typów podstawowych, lub gdy algorytm wymaga kopii.

Te zasady są proste, i stały mnie w dobrym miejscu. Pozostawiam zasady używania inteligentnych wskaźników (ale proszę, nie auto_ptr) jako członków klasy innym.

 6
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-21 11:08:42

Yes to: Czy prawdą jest, że obiekt zawierający odniesienie nie powinien być przypisywany, ponieważ odniesienie nie powinno być zmieniane po zainicjowaniu?

Moje zasady dla członków danych:

  • nigdy nie używaj referencji, ponieważ uniemożliwia przypisanie
  • jeśli twoja klasa jest odpowiedzialna za usunięcie, użyj scoped_ptr Boosta (który jest bezpieczniejszy niż auto_ptr)
  • w przeciwnym razie użyj wskaźnika lub wskaźnika const
 4
Author: pts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-21 11:39:28

Zazwyczaj używałbym wskaźnika w danych członkowskich tylko wtedy, gdy wskaźnik może być null lub może się zmienić. Czy istnieją inne powody, aby preferować wskaźniki niż odniesienia dla członków danych?

Tak. Czytelność kodu. Wskaźnik czyni bardziej oczywistym, że członek jest referencją (jak na ironię :)), a nie zawartym obiektem, ponieważ kiedy go używasz, musisz go usunąć. Wiem, że niektórzy ludzie myślą, że to staroświeckie, ale nadal myślę, że to po prostu zapobiec zamieszanie i błędy.
 2
Author: Dani van der Meer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-21 10:19:07

Odradzam użytkownikom danych referencyjnych, ponieważ nigdy nie wiesz, kto będzie czerpał z twojej klasy i co mogą chcieć zrobić. Mogą nie chcieć korzystać z obiektu odniesienia, ale jako odniesienie zmusiłeś ich do podania poprawnego obiektu. Zrobiłem to sobie na tyle, żeby przestać używać danych referencyjnych.

 2
Author: StephenD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-05 16:12:32