Szybkie pytanie: z punktu widzenia projektowania, dlaczego w C ++ nie ma klasy podstawowej, która jest matką wszystkiego, co zwykle jest object
w innych językach?
c++
language-design
Slezica
źródło
źródło
typedef
s. W przypadku bardziej ogólnej hierarchii klas można po prostu użyćobject
lubiterator
.Odpowiedzi:
Ostateczne orzeczenie znajduje się w FAQ Stroustrupa . Krótko mówiąc, nie przekazuje żadnego znaczenia semantycznego. Będzie to miało swój koszt. Szablony są bardziej przydatne w przypadku kontenerów.
źródło
Objects must be heap-allocated to be polymorphic
- Myślę, że to stwierdzenie nie jest ogólnie poprawne. Zdecydowanie możesz utworzyć instancję klasy polimorficznej na stosie i przekazać ją jako wskaźnik do jednej z jej klas bazowych, uzyskując zachowanie polimorficzne.Foo
na odniesienie do -Bar
gdyBar
nie dziedziczyFoo
, deterministycznie zgłasza wyjątek. W C ++ próba rzucenia wskaźnika na Foo na wskaźnik na Bar może wysłać robota w przeszłość, aby zabić Sarah Connor.Najpierw zastanówmy się, dlaczego chcesz mieć klasę bazową. Przychodzi mi do głowy kilka różnych powodów:
Są to dwa dobre powody, dla których języki marki Smalltalk, Ruby i Objective-C mają klasy bazowe (technicznie rzecz biorąc, Objective-C tak naprawdę nie ma klasy bazowej, ale pod każdym względem ma).
W przypadku # 1 potrzeba klasy bazowej, która ujednolica wszystkie obiekty w jednym interfejsie, została wyeliminowana przez włączenie szablonów w C ++. Na przykład:
void somethingGeneric(Base); Derived object; somethingGeneric(object);
jest niepotrzebne, gdy można zachować integralność typu przez cały czas za pomocą polimorfizmu parametrycznego!
template <class T> void somethingGeneric(T); Derived object; somethingGeneric(object);
W przypadku # 2, podczas gdy w Objective-C procedury zarządzania pamięcią są częścią implementacji klasy i są dziedziczone z klasy bazowej, zarządzanie pamięcią w C ++ jest wykonywane przy użyciu kompozycji, a nie dziedziczenia. Na przykład możesz zdefiniować inteligentne opakowanie wskaźnika, które będzie wykonywać zliczanie referencji na obiektach dowolnego typu:
template <class T> struct refcounted { refcounted(T* object) : _object(object), _count(0) {} T* operator->() { return _object; } operator T*() { return _object; } void retain() { ++_count; } void release() { if (--_count == 0) { delete _object; } } private: T* _object; int _count; };
Wtedy zamiast wywoływać metody w samym obiekcie, będziesz wywoływał metody w jego opakowaniu. Pozwala to nie tylko na bardziej ogólne programowanie: pozwala również na oddzielenie problemów (ponieważ w idealnym przypadku obiekt powinien być bardziej zainteresowany tym, co powinien robić, niż tym, jak jego pamięć powinna być zarządzana w różnych sytuacjach).
Wreszcie, w języku, który ma zarówno prymitywy, jak i rzeczywiste obiekty, takie jak C ++, korzyści z posiadania klasy bazowej (spójny interfejs dla każdej wartości) są tracone, ponieważ wtedy masz pewne wartości, które nie mogą być zgodne z tym interfejsem. Aby użyć prymitywów w takiej sytuacji, musisz podnieść je do obiektów (jeśli Twój kompilator nie zrobi tego automatycznie). Stwarza to wiele komplikacji.
A więc krótka odpowiedź na twoje pytanie: C ++ nie ma klasy bazowej, ponieważ mając polimorfizm parametryczny za pośrednictwem szablonów, nie musi.
źródło
object
(System.Object
), ale nie muszą. Do kompilatoraint
iSystem.Int32
są aliasami i mogą być używane zamiennie; w razie potrzeby środowisko wykonawcze obsługuje boks.std::shared_ptr
którego należy użyć zamiast tego.Dominującym paradygmatem dla zmiennych C ++ jest przekazywanie przez wartość, a nie przekazywanie przez odniesienie. Wymuszenie wyprowadzenia wszystkiego z korzenia
Object
spowodowałoby, że przekazywanie ich przez wartość byłoby błędem ipse facto.(Ponieważ zaakceptowanie obiektu według wartości jako parametru, z definicji spowodowałoby wycięcie go i usunięcie jego duszy).
To jest niepożądane. C ++ sprawia, że myślisz o tym, czy potrzebujesz semantyki wartości, czy referencji, dając ci wybór. To wielka rzecz w obliczeniach wydajnościowych.
źródło
Object
byłby stosunkowo niewielki.Problem w tym, że taki typ JEST w C ++! Tak jest
void
. :-) Dowolny wskaźnik może być bezpiecznie niejawnie rzutowany navoid *
, w tym wskaźniki do typów podstawowych, klas bez wirtualnej tabeli i klas z wirtualną tabelą.Ponieważ powinien być kompatybilny ze wszystkimi tymi kategoriami obiektów,
void
sam w sobie nie może zawierać metod wirtualnych. Bez funkcji wirtualnych i RTTI nie można uzyskać żadnych przydatnych informacji o typievoid
(pasuje do KAŻDEGO typu, więc można powiedzieć tylko rzeczy, które są prawdziwe dla KAŻDEGO typu), ale funkcje wirtualne i RTTI sprawiłyby, że proste typy byłyby bardzo nieskuteczne i powstrzymałyby C ++ przed byciem język odpowiedni do programowania niskopoziomowego z bezpośrednim dostępem do pamięci itp.Więc jest taki typ. Po prostu zapewnia bardzo minimalistyczny (w rzeczywistości pusty) interfejs ze względu na niskopoziomowy charakter języka. :-)
źródło
void
.void
nie będzie ona kompilowana i otrzymasz ten błąd . W Javie możesz mieć zmienną typuObject
i to zadziała. Na tym polega różnica międzyvoid
typem „prawdziwym” a. Być może to prawda, żevoid
jest to typ bazowy dla wszystkiego, ale ponieważ nie zawiera żadnych konstruktorów, metod ani pól, nie ma sposobu, aby stwierdzić, czy istnieje, czy nie. Tego twierdzenia nie można udowodnić ani obalić.void
jest to typ (i odkryłem dość sprytne użycie? :
), ale wciąż jest daleko od bycia uniwersalną klasą bazową. Po pierwsze jest to „niepełny typ, którego nie można ukończyć”, a po drugie, nie mavoid&
typu.C ++ jest językiem silnie typizowanym. Zastanawiające jest jednak to, że nie ma ona uniwersalnego typu obiektu w kontekście specjalizacji szablonowej.
Weźmy na przykład wzór
template <class T> class Hook; template <class ReturnType, class ... ArgTypes> class Hook<ReturnType (ArgTypes...)> { ... ReturnType operator () (ArgTypes... args) { ... } };
który można utworzyć jako
Hook<decltype(some_function)> ...;
Teraz załóżmy, że chcemy tego samego dla określonej funkcji. Lubić
template <auto fallback> class Hook; template <auto fallback, class ReturnType, class ... ArgTypes> class Hook<ReturnType fallback(ArgTypes...)> { ... ReturnType operator () (ArgTypes... args) { ... } };
z wyspecjalizowaną instancją
Ale niestety, nawet jeśli klasa T może zastępować dowolny typ (klasa lub nie) przed specjalizacją, nie ma odpowiednika
auto fallback
(używam tej składni jako najbardziej oczywistej generycznej innej niż typ w tym kontekście), który mógłby zastąpić dowolny argument szablonu innego niż typ przed specjalizacją.Więc ogólnie ten wzorzec nie przenosi z argumentów szablonu typu do argumentów szablonu innego niż typ.
Podobnie jak w przypadku wielu zakrętów w języku C ++, odpowiedź prawdopodobnie brzmi „żaden członek komitetu o tym nie pomyślał”.
źródło
ReturnType fallback(ArgTypes...)
zadziałałoby, byłby to zły projekt.template <class T, auto fallback> class Hook; template <class ReturnType, class ... ArgTypes> class Hook<ReturnType (ArgTypes...), ReturnType(*fallback)(ArgTypes...)> ...
robi to, co chcesz i oznaczają parametry szablonuHook
są spójne rodzajuC ++ początkowo nosił nazwę „C z klasami”. Jest to rozwinięcie języka C, w przeciwieństwie do innych bardziej nowoczesnych rzeczy, takich jak C #. I nie można zobaczyć C ++ jako języka, ale jako podstawę języków (tak, pamiętam książkę Scotta Meyersa Efektywne C ++).
Sam C jest mieszanką języków, języka programowania C i jego preprocesora.
C ++ dodaje kolejną mieszankę:
podejście klasowe / obiektowe
szablony
plik STL
Osobiście nie lubię niektórych rzeczy, które pochodzą bezpośrednio z C do C ++. Jednym z przykładów jest funkcja wyliczenia. Sposób, w jaki C # pozwala deweloperowi na jego użycie, jest o wiele lepszy: ogranicza wyliczenie w swoim własnym zakresie, ma właściwość Count i jest łatwo iterowalny.
Ponieważ C ++ chciał być retrokompatybilny z C, projektant był bardzo liberalny, pozwalając językowi C na wejście w całości do C ++ (są pewne subtelne różnice, ale nie pamiętam nic, co można by zrobić za pomocą kompilatora C, nie można zrobić przy użyciu kompilatora C ++).
źródło