Oto uproszczony przykład ilustrujący pytanie:
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 narzekał o elemencie referencyjnym: lint # 1725 . To mówi o dbaniu o domyślną kopię i przypisania, co jest wystarczająco uczciwe, ale domyślna kopia i przypisanie są również złe w przypadku wskaźników, więc nie ma z tego żadnej korzyści.
Zawsze staram się używać odnośników 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 składowych klasy, która jest właścicielem wskaźnika, i przekazuję obiekt jako odniesienie.
Generalnie użyłbym wskaźnika w danych składowych tylko wtedy, gdy wskaźnik mógłby być zerowy lub mógłby się zmienić. Czy są jakieś inne powody, aby preferować wskaźniki zamiast referencji dla członków danych?
Czy prawdą jest, że obiekt zawierający odniesienie nie powinien być przypisywany, ponieważ odwołanie nie powinno być zmieniane po zainicjowaniu?
źródło
Odpowiedzi:
Unikaj członków referencyjnych, ponieważ ograniczają to, co może zrobić implementacja klasy (w tym, jak wspomniałeś, zapobieganie implementacji operatora przypisania) i nie zapewniają żadnych korzyści z tego, co może zapewnić klasa.
Przykładowe problemy:
do C ++ 0x, w każdym razieedytuj: C ++ ma teraz konstruktory delegujące ).
operator itp.), Ale zachowuje się jak wskaźnik (może dyndać) - więc np. Przewodnik po stylach Google odradza goźródło
Moja własna praktyczna zasada:
źródło
Obiekty rzadko powinny pozwalać na przypisywanie i inne rzeczy, takie jak porównanie. Jeśli weźmiesz pod uwagę model biznesowy z takimi obiektami jak „Dział”, „Pracownik”, „Dyrektor”, trudno wyobrazić sobie przypadek, w którym jeden pracownik zostanie przydzielony drugiemu.
Dlatego w przypadku obiektów biznesowych bardzo dobrze jest opisywać relacje jeden do jednego i jeden do wielu jako odniesienia, a nie wskaźniki.
I prawdopodobnie można opisać relację jeden lub zero jako wskaźnik.
Więc nie ma „nie możemy przypisać”, a następnie czynnik.
Wielu programistów przyzwyczaja się po prostu do wskaźników i dlatego znajdą argument, aby uniknąć używania 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 mieć wartość zero, to prawdopodobnie wskaźnik jest używany jako rodzaj flagi, co jest złe, ponieważ każdy obiekt musi odgrywać swoją rolę.
źródło
Używaj odniesień, kiedy możesz, i wskaźników, kiedy musisz.
źródło
W kilku ważnych przypadkach przypisywalność po prostu nie jest potrzebna. Są to często lekkie opakowania algorytmów, które ułatwiają obliczenia bez opuszczania zakresu. Takie obiekty są głównymi kandydatami na członków referencyjnych, ponieważ możesz mieć pewność, że zawsze zawierają ważne referencje i nigdy nie trzeba ich kopiować.
W takich przypadkach upewnij się, że operator przypisania (a często także konstruktor kopiujący) nie nadaje się do użytku (przez dziedziczenie
boost::noncopyable
lub deklarowanie ich jako prywatne).Jednakże, jak już skomentował użytkownik pts, to samo nie dotyczy większości innych obiektów. W tym przypadku korzystanie z członków referencyjnych może być ogromnym problemem i należy go zasadniczo unikać.
źródło
Ponieważ wszyscy wydają się rozdawać ogólne zasady, zaproponuję dwa:
Nigdy, przenigdy nie używaj odwołań jako członków klasy. Nigdy nie robiłem tego w swoim własnym kodzie (z wyjątkiem udowodnienia sobie, że miałem rację w tej regule) i nie wyobrażam sobie przypadku, w którym bym to zrobił. Semantyka jest zbyt zagmatwana i naprawdę nie do tego zostały zaprojektowane referencje.
Zawsze, zawsze używaj odwołań podczas przekazywania parametrów do funkcji, z wyjątkiem typów podstawowych lub gdy algorytm wymaga kopii.
Te zasady są proste i bardzo mi pomogły. Pozostawiam tworzenie zasad używania inteligentnych wskaźników (ale proszę, nie auto_ptr) jako członków klasy dla innych.
źródło
Tak na: Czy prawdą jest, że obiekt zawierający odniesienie nie powinien być przypisywany, ponieważ odwołanie nie powinno być zmieniane po zainicjowaniu?
Moje praktyczne zasady dla członków danych:
źródło
Tak. Czytelność Twojego kodu. Wskaźnik sprawia, że jest bardziej oczywiste, że element członkowski jest referencją (jak na ironię :)), a nie obiektem zawartym, ponieważ kiedy go używasz, musisz go usunąć. Wiem, że niektórzy myślą, że jest to staroświeckie, ale nadal uważam, że to po prostu zapobiega pomyłkom i błędom.
źródło
Odradzam członkom danych referencyjnych, ponieważ nigdy nie wiadomo, kto będzie pochodził z Twojej klasy i co mogą chcieć zrobić. Mogą nie chcieć korzystać z obiektu, do którego się odwołuje, ale będąc odniesieniem, zmusiłeś ich do podania prawidłowego obiektu. Zrobiłem to sobie na tyle, aby przestać używać członków danych referencyjnych.
źródło
Jeśli dobrze rozumiem pytanie ...
Odwołanie jako parametr funkcji zamiast wskaźnika: Jak zauważyłeś, wskaźnik nie wyjaśnia, kto jest właścicielem czyszczenia / inicjalizacji wskaźnika. Preferuj punkt współdzielony, gdy chcesz mieć wskaźnik, jest to część C ++ 11 i słaby_ptr, gdy ważność danych nie jest gwarantowana przez cały okres istnienia klasy akceptującej wskazane dane .; również część C ++ 11. Użycie odwołania jako parametru funkcji gwarantuje, że referencja nie jest pusta. Aby obejść ten problem, musisz obalić funkcje języka, a nas nie obchodzą luźne kodery dział.
Odniesienie do zmiennej składowej: Jak wyżej w odniesieniu do ważności danych. Gwarantuje to, że wskazane dane, a następnie przywoływane, są ważne.
Przeniesienie odpowiedzialności za ważność zmiennej do wcześniejszego punktu w kodzie nie tylko czyści późniejszy kod (w naszym przykładzie klasa A), ale także czyni go jasnym dla osoby używającej.
W twoim przykładzie, który jest nieco zagmatwany (naprawdę spróbuję znaleźć jaśniejszą implementację), A używane przez B jest gwarantowane przez cały okres życia B, ponieważ B jest członkiem A, więc odniesienie to wzmacnia i jest (być może) bardziej jasne.
I na wszelki wypadek (co jest mało prawdopodobne, ponieważ nie miałoby to żadnego sensu w kontekście twojego kodu), inna alternatywa, użycie parametru A bez odniesienia, bez wskaźnika, skopiowałaby A i uczyniłaby paradygmat bezużytecznym - ja naprawdę nie myśl, że masz to na myśli jako alternatywa.
Ponadto gwarantuje to również, że nie będziesz w stanie zmienić danych, do których się odwołujesz, gdzie można zmodyfikować wskaźnik. Wskaźnik do stałej działałby tylko wtedy, gdyby odnośnik / wskaźnik do danych nie był zmienny.
Wskaźniki są przydatne, jeśli nie można zagwarantować ustawienia parametru A dla B lub można go ponownie przypisać. Czasami słaby wskaźnik jest po prostu zbyt rozwlekły dla implementacji, a duża liczba osób albo nie wie, co to jest słaby_ptr, albo po prostu ich nie lubi.
Rozerwij tę odpowiedź, proszę :)
źródło