Skip to content

Commit 3802dc9

Browse files
committed
learncpp virtual
1 parent b2d1c53 commit 3802dc9

1 file changed

Lines changed: 235 additions & 0 deletions

File tree

content/posts/cpp-learn.md

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3394,3 +3394,238 @@ int main()
33943394
### 20.7. Multiple inheritance
33953395
- C++ provides the ability to do multiple inheritance. Multiple inheritance enables a derived class to inherit members from more than one parent.
33963396
- Avoid multiple inheritance unless alternatives lead to more complexity.
3397+
3398+
## 21. Virtual Functions - Polymorphism
3399+
### 21.1. Pointers and references to the base class of derived objects
3400+
- **Pointers, references, and derived classes:** We can not only assign `Derived pointers and references` to `Derived objects`, but also assign `Base pointers and references` to `Derived objects`.
3401+
> **chỉ có thể gọi các hàm/thành viên có trong Base -> need virtual**
3402+
> Derived chứa phần dữ liệu của Base ở đầu đối tượng, nên con trỏ Base* có thể trỏ đến vùng đó mà không sai về mặt địa chỉ.
3403+
> Compiler đảm bảo phần đầu tiên của Derived có cùng layout với Base.
3404+
- e.g.
3405+
```cpp
3406+
#include <iostream>
3407+
3408+
int main()
3409+
{
3410+
Derived derived{ 5 };
3411+
Derived& rDerived{derived};
3412+
Derived* rDerived{&derived};
3413+
3414+
3415+
// These are both legal!
3416+
Base& rBase{ derived }; // rBase is an lvalue reference (not an rvalue reference)
3417+
Base* pBase{ &derived };
3418+
3419+
std::cout << "derived is a " << derived.getName() << " and has value " << derived.getValue() << '\n';
3420+
std::cout << "rBase is a " << rBase.getName() << " and has value " << rBase.getValue() << '\n';
3421+
std::cout << "pBase is a " << pBase->getName() << " and has value " << pBase->getValue() << '\n';
3422+
3423+
return 0;
3424+
}
3425+
3426+
```
3427+
- **Use for pointers and references to base classes:** let us pass any derived object to a single function instead of writing one for each class.
3428+
However, since `rBase` is an `Based reference`, calling `Base.fucntion()` runs `Base::fucntion()` unless the function is `virtual`.
3429+
3430+
### 21.2. Virtual functions and polymorphism
3431+
- **Virtual functions:**
3432+
- is a special type of member function that, when called, resolves to the `most-derived version` of the function for the actual type of the object being referenced or pointed to.
3433+
- is considered a match if it has the same signature (name, parameter types, and whether it is const) and return type as the base version of the function. Such functions are called overrides.
3434+
- to make a function virtual, simply place the `virtual` keyword before the function declaration.
3435+
3436+
- e.g.
3437+
```cpp
3438+
#include <iostream>
3439+
#include <string_view>
3440+
3441+
class Base
3442+
{
3443+
public:
3444+
virtual std::string_view getName() const { return "Base"; } // note addition of virtual keyword
3445+
virtual ~Base() = default; // virtual destructor
3446+
};
3447+
3448+
class Derived: public Base
3449+
{
3450+
public:
3451+
virtual std::string_view getName() const { return "Derived"; }
3452+
};
3453+
3454+
int main()
3455+
{
3456+
Derived derived {};
3457+
Base& rBase{ derived };
3458+
std::cout << "rBase is a " << rBase.getName() << '\n';
3459+
3460+
return 0;
3461+
}
3462+
3463+
// RESULT: rBase is a Derived
3464+
```
3465+
- **Return types of virtual functions:** the return type of a virtual function and its override must match, otherwise the compilation will fail.
3466+
- **Do not call virtual functions from constructors or destructors:** because of the class hadn’t even been created yet
3467+
3468+
- **polymorphism** refers to the ability of an entity to have multiple forms (the term “polymorphism” literally means “many forms”)
3469+
- **Compile-time polymorphism** refers to forms of polymorphism that are resolved by the compiler. These include **function overload resolution**, as well as **template resolution**.
3470+
- **Runtime polymorphism** refers to forms of polymorphism that are resolved at runtime. This includes virtual function resolution.
3471+
3472+
### 21.3. The override and final specifiers, and covariant return types
3473+
- **The override specifier:** the `override` specifier can be applied to any virtual function to tell the compiler to enforce that the function is an override.
3474+
- `override` specifier is placed at the end of a member function declaration.
3475+
- If a member function is const and an override, the const must come before override.
3476+
- We should use the virtual keyword on virtual functions in a base class.
3477+
3478+
- **The final specifier:** The final specifier can be used to tell the compiler to enforce a virtual function to be unable to override.
3479+
- used in the same place the override specifier is
3480+
- e.g.
3481+
```cpp
3482+
#include <iostream>
3483+
using namespace std;
3484+
3485+
// Base class
3486+
class Animal {
3487+
public:
3488+
virtual void speak() const { cout << "Animal speaking\n"; }
3489+
virtual void move() { cout << "Animal moving\n"; }
3490+
};
3491+
3492+
// Derived class
3493+
class Dog : public Animal {
3494+
public:
3495+
// override: tells compiler this must override a virtual function
3496+
void speak() const override { cout << "Dog barking\n"; }
3497+
3498+
// final: prevents further overriding in subclasses
3499+
void move() final { cout << "Dog running\n"; }
3500+
};
3501+
3502+
// Further derived class
3503+
class Puppy : public Dog {
3504+
public:
3505+
// OK: override speak()
3506+
void speak() const override { cout << "Puppy yapping\n"; }
3507+
3508+
// ERROR: move() is final in Dog, cannot override
3509+
// void move() override { cout << "Puppy hopping\n"; }
3510+
};
3511+
3512+
int main() {
3513+
Animal* a = new Puppy();
3514+
a->speak(); // → "Puppy yapping"
3515+
a->move(); // → "Dog running"
3516+
delete a;
3517+
}
3518+
3519+
```
3520+
- **Covariant return types**
3521+
```cpp
3522+
#include <iostream>
3523+
#include <string_view>
3524+
3525+
class Base
3526+
{
3527+
public:
3528+
// This version of getThis() returns a pointer to a Base class
3529+
virtual Base* getThis() { std::cout << "called Base::getThis()\n"; return this; }
3530+
void printType() { std::cout << "returned a Base\n"; }
3531+
};
3532+
3533+
class Derived : public Base
3534+
{
3535+
public:
3536+
// Normally override functions have to return objects of the same type as the base function
3537+
// However, because Derived is derived from Base, it's okay to return Derived* instead of Base*
3538+
Derived* getThis() override { std::cout << "called Derived::getThis()\n"; return this; }
3539+
void printType() { std::cout << "returned a Derived\n"; }
3540+
};
3541+
3542+
int main()
3543+
{
3544+
Derived d{};
3545+
Base* b{ &d };
3546+
d.getThis()->printType(); // calls Derived::getThis(), returns a Derived*, calls Derived::printType
3547+
b->getThis()->printType(); // calls Derived::getThis(), returns a Base*, calls Base::printType
3548+
3549+
return 0;
3550+
}
3551+
```
3552+
3553+
### 21.4. Virtual destructors, virtual assignment, and overriding virtualization
3554+
- **Virtual destructors:** in the case that you will want to provide your own destructor (particularly if the class needs to deallocate memory).
3555+
- e.g.
3556+
```cpp
3557+
#include <iostream>
3558+
class Base
3559+
{
3560+
public:
3561+
// virtual ~Base() = default; // generate a virtual default destructor
3562+
virtual ~Base() // note: virtual
3563+
{
3564+
std::cout << "Calling ~Base()\n";
3565+
}
3566+
};
3567+
3568+
class Derived: public Base
3569+
{
3570+
private:
3571+
int* m_array {};
3572+
3573+
public:
3574+
Derived(int length)
3575+
: m_array{ new int[length] }
3576+
{
3577+
}
3578+
3579+
virtual ~Derived() // note: virtual
3580+
{
3581+
std::cout << "Calling ~Derived()\n";
3582+
delete[] m_array;
3583+
}
3584+
};
3585+
3586+
int main()
3587+
{
3588+
Derived* derived { new Derived(5) };
3589+
Base* base { derived };
3590+
3591+
delete base;
3592+
3593+
return 0;
3594+
}
3595+
3596+
// OUTPUT:
3597+
// Calling ~Derived()
3598+
// Calling ~Base()
3599+
```
3600+
3601+
- **Ignoring virtualization**: incase we want to ignore the virtualization of a function.
3602+
- e.g.
3603+
```cpp
3604+
#include <string_view>
3605+
class Base
3606+
{
3607+
public:
3608+
virtual ~Base() = default;
3609+
virtual std::string_view getName() const { return "Base"; }
3610+
};
3611+
3612+
class Derived: public Base
3613+
{
3614+
public:
3615+
virtual std::string_view getName() const { return "Derived"; }
3616+
};
3617+
3618+
#include <iostream>
3619+
int main()
3620+
{
3621+
Derived derived {};
3622+
const Base& base { derived };
3623+
3624+
// Calls Base::getName() instead of the virtualized Derived::getName()
3625+
std::cout << base.Base::getName() << '\n';
3626+
3627+
return 0;
3628+
}
3629+
```
3630+
>If we intend our class to be inherited from, make sure our destructor is virtual and public.
3631+
If we do not intend our class to be inherited from, mark our class as final.

0 commit comments

Comments
 (0)