2012-05-11 22 views
7

Ogólnie wydaje się korzystne w C++ tego kodu, które funkcje, które mają parametr do polimorficznych obiektu trwa wskaźnik do niego:Dlaczego wskaźniki do klasy bazowej są bardziej preferowane niż odniesienia?

class Base {}; 
class DerivedA : public Base {}; 
class DerivedB : public Base {}; 

void OperateOnObject(Base* obj) {}; 

vector<Base*> objects; 
objects.push_back(new DerivedA()); 
objects.push_back(new DerivedB()); 

for (size_t i=0; i<objects.size(); i++) { 
    OperateOnObject(objects[i]); 
} 

Czemu OperateOnObject() jest zwykle napisane podjąć wskaźnik do Base zamiast odniesienie? Czy istnieją potencjalne problemy z cięciem? (np. vtable ginie)

Widziałem this response dla podobnie brzmiącego pytania, ale wydaje się, że nie dotyczy to tego samego problemu.

+3

We wszystkich projektach, w których pracowałem, preferencje wskaźnika w stosunku do referencji zawsze zależały od czegoś innego niż od polimofizmu ... – PlasmaHH

+0

Dodatkowym czynnikiem jest to, że w VisualStudio pojawia się dużo więcej informacji o debugowaniu dla wskaźników (np. wpisz info dla najbardziej wyprowadzonego typu) niż dla referencji, więc jest to również czynnik preferujący jeden względem drugiego. –

+1

W dobrym projekcie (nie) umiejętności IDE/debuggera nie powinny wpływać na podstawowe właściwości projektu. – PlasmaHH

Odpowiedz

8

Referencje nie stanowią problemu z cięciem - pod tym względem są równie dobre, jak wskazówki. Nie ma "twardego" powodu, aby preferować wskaźniki, chyba że chcesz, aby semantyka NULL była dostępna dla twojego kodu (tj. Zdolność do przekazywania "nic", czego nie ma, jeśli używasz referencji).

Myślę, że typy wskaźników są często używane jako parametry funkcji polimorficznych, aby dopasować semantyczną kolekcję: ponieważ nie można utworzyć odniesień vector, kod zakończy się koniecznością użycia wskaźników dereferencji uzyskanych ze zbiorów przed przekazaniem ich do funkcji . To może być denerwujące.

+0

OK, tak naprawdę to tylko kwestia stylistyczna, nie ma żadnego technicznego powodu. Wydaje się, że zgadza się to z reakcją Steve'a –

9

Z pewnością nie ma problemu z cięciem - twoje klasy nie wymagają vtables, ponieważ nie mają funkcji wirtualnych, ale nawet jeśli je mają, pass-by-reference nie kopiuje obiektu, a tym samym nie robi " ciąć cokolwiek.

Możesz wykonywać połączenia wirtualne za pomocą referencji dokładnie tak, jak to możliwe za pomocą wskaźników. O ile wiem, nie ma praktycznego powodu, tylko dla stylu.

Może to zrobić z faktem, że polimorficzne obiekty są zazwyczaj stworzony przez wskaźnik z new i oczywiście muszą być przechowywane w pojemnikach przez wskaźnik (lub inteligentnego wskaźnika), ponieważ nie można mieć pojemnik z referencjami. Może wtedy wydawać się bardziej konsekwentne manipulowanie nimi za pomocą wskaźnika zawsze.

Ponadto, jeśli chcesz używać OperateOnObject w standardowym algorytmie, takim jak for_each, musisz zaakceptować typ elementu kontenera, który jest wskaźnikiem, lub musisz go zawinąć w adapter funkcyjny, który dereferencja. Standardowy C++ nie ma tego adaptera, więc jest to świat trudu, by oprzeć swój styl na nim.

pokrewne: zobacz, co się stanie, jeśli OperateOnObject bierze odniesienie i iteracyjne swoje wektorowej za pomocą iteratorów:

for (vector<Base*>::iterator i = objects.begin(); i != objects.end(); ++i) { 
    OperateOnObject(**i); 
} 

20. czas, że dwukrotnie wskazanie pośrednie denerwuje lub dezorientuje cię, będzie zmienić podpis OperateOnObject; -)

Na marginesie, niektóre przewodniki stylów ostrzegają przed przekazywaniem referencji bez stałych, niezależnie od tego, czy typ jest polimorficzną klasą podstawową, na podstawie której nie można od razu odróżnić przejścia od referencji do podania -do-wartości, gdy czytasz kod na stronie połączenia. Więc wolą, aby OperateOnObject wziął wskaźnik mimo to.

Osobiście uważam, że argument ten jest trochę słaby - nazwa funkcji powinna informować cię z grubsza o tym, co robi, a zwłaszcza oświadczenie takie jak ChangeTheObject(my_object); nie jest tak subtelnie podpowiedziane dla mnie, ponieważ nie używa żadnej wartości zwracanej , musi zmienić swój argument. Ale zgadzam się, że jeśli odpowiednio podążasz za stylem, możesz wyraźnie i konsekwentnie odróżniać mutatory od czystych funkcji.

Powiązane problemy