Inheritance and virtual functions
لم يُترجم بعد؛ يُعرض باللغة الإنجليزية
A derived class is built on a base class: a Dog is an Animal plus more. Follow one Dog object as main makes it and uses it through a pointer to Animal: watch the Animal part get built before the Dog part, a normal call go where the pointer's type says, a virtual call go where the object really is (through the hidden vptr and vtable), override catch a typo, and the destructors run Dog first, then Animal.
5 أسئلة سريعة
اختر إجابة لتعرف السبب. تُحفَظ نتيجتك مع تقدّمك.
01Dog derives from Animal (struct Dog : Animal). What does a Dog object hold?
A derived object contains a base class subobject. make_unique<Dog>() builds the Animal part first (legs = 4), then Dog's own members (tricks = 2), then runs the body of Dog's constructor.
02p has type std::unique_ptr<Animal> and points at a Dog. kind is not virtual. What does p->kind() print?
For a non-virtual function the compiler picks the version from the pointer's type, Animal, before the program runs. Dog's kind only hides Animal's; it is used when you call it through a Dog.
03speak is virtual and Dog overrides it. What does p->speak() print, and when is that decided?
A virtual call runs the version that belongs to the object's real type. Compilers do this with a hidden pointer in the object (the vptr) to its class's table of virtual functions (the vtable).
04In Dog you mistype void Speak() override { ... } (capital S). What happens?
override asks the compiler to check that the function really replaces a virtual function of the base. Animal has no virtual Speak, so it is a compile error. Without override the typo compiles and p->speak() quietly runs Animal's version.
05Why does Animal need a virtual destructor in this program?
std::unique_ptr<Animal> deletes the object through an Animal pointer. Deleting a derived object through a base pointer is undefined behaviour unless the base's destructor is virtual. With it, ~Dog runs first, then ~Animal: reverse order of construction.
المسرد
- Base class
- The class another class is built on. Animal is the base class of Dog.
- Derived class
- A class built on a base class with struct Dog : Animal (or class Dog : public Animal). It inherits the base's members and can add or override its own.
- Inheritance
- Making a new class from an existing one: a Dog is an Animal plus more, so a Dog can be used wherever an Animal is expected.
- Virtual function
- A member function marked virtual. A call through a base pointer or reference runs the version of the object's real type, decided while the program runs.
- override
- Written after a member function to say it replaces a virtual function of the base. The compiler reports an error if it does not.
- Polymorphism
- One piece of code working with objects of many types, each behaving its own way; in C++ usually through virtual functions.
- vtable and vptr
- How compilers implement virtual calls: each class with virtual functions has a table of them (the vtable), and each object holds a hidden pointer to its class's table (the vptr).
- Virtual destructor
- A destructor marked virtual, so deleting a derived object through a base pointer runs the derived destructor first, then the base's.
الملاحظات والمصادر
- The two cards are one file, zoo.cpp, which starts with #include <cstdio>, #include <memory>, using std::puts; (the last line lets the code write puts instead of std::puts). Build it with g++ -std=c++20 zoo.cpp -o zoo and run ./zoo: it prints animal, Woof, ~Dog and ~Animal on four lines.
- The C++ standard says which function a virtual call runs, not how. The vptr and vtable shown here are how GCC, Clang and MSVC do it. With GCC and Clang on 64-bit Linux or macOS a Dog takes 16 bytes: an 8-byte vptr, then legs, then tricks (MSVC uses 24, because it does not reuse the padding at the end of Animal's part). Real tables hold more than the two slots drawn.
- Precisely: a Dog's base class Animal is initialized first, then Dog's own members (tricks), then the body of Dog's constructor. Destruction runs the other way: ~Dog's body, then Dog's members, then the Animal part.
- Dog's kind has the same name as Animal's but Animal's is not virtual, so Dog's only hides it: through a Dog (Dog d; d.kind();) it prints dog, through an Animal pointer it prints animal.
- Exact error messages differ between compilers. For the Speak typo, GCC says marked 'override', but does not override; Clang says only virtual member functions can be marked 'override'.
- Deleting an object through a pointer to its base class is undefined behaviour unless the base class's destructor is virtual. std::unique_ptr<Animal> deletes through Animal*, so ~Animal must be virtual here.
- cppreference.com: Derived classes
- cppreference.com: virtual function specifier
- cppreference.com: override specifier
- cppreference.com: Constructors and member initializer lists (initialization order)
- cppreference.com: Destructors (destruction sequence, virtual destructors)
- cppreference.com: delete expression
- cppreference.com: std::unique_ptr
- Standard C++ Foundation (isocpp.org): Inheritance: virtual functions FAQ
- Standard C++ Foundation (isocpp.org): Inheritance basics FAQ
- C++ Core Guidelines: C.35 (virtual base destructor) and C.128 (override)
- Itanium C++ ABI: virtual table layout (used by GCC and Clang)
اختبار يتاح بعد الخطوة الأخيرة.