C++虚函数构成多态详解
为什么需要虚函数
1 |
|
以这个简单的继承关系来分析,派生类与基类存在一个同名成员函数void fun()
在main函数中,声明基类指针指向派生类的对象,那么在调用p -> fun()
会指向什么?
按照惯常的逻辑,指针指向派生类对象那应该调用的是派生类的成员函数和成员函数,但实际上输出的是基类的成员函数,也就是说,基类的指针无法直接访问派生类成员函数
那么如何让基类指针指向基类对象就使用基类的所有成员,指向派生类对象就使用派生类所有成员,也就是实现基类指针的多种形态,就是多态性
就要利用虚函数,要在基类成员函数前加上virtual
关键字
1 |
|
这样的话,上述代码
1 |
|
C++虚函数的目的就是:可以通过基类指针对所有派生类(包括直接派生和间接派生)的成员变量和成员函数进行“全方位”的访问,尤其是成员函数。如果没有多态,我们只能访问成员变量。
虚析构函数的必要性
学习虚函数时,书本上提到析构函数有时必须要声明为虚函数,这个必须是什么意思,让我百思不得其解
在查阅资料后,才知道这个是一种典型的内存泄露的问题
1 |
|
这个代码可以看到析构函数起到一个释放内存的作用,在对象的生命周期结束后会自动释放构造函数时分配的内存
可以看到main函数中用了两种方式创建派生类的对象
1 |
|
那么运行结果是什么呢?
Base constructor
Derived constructorBase destructor
Base constructor
Derived constructor
Derived destructor
Base destructor
delete pb只调用了基类的析构函数,这样就导致了创建的100个char类型的内存空间无法释放,就是典型的内存泄漏
- 为什么delete基类指针没有调用派生类的析构函数?
因为编译器会根据指针类型来确认要调用的对象,所以不管它指向基类对象还是派生类的析构函数,始终调用基类构造函数 - 为何delete派生类指针会同时调用两类的析构函数
因为这个是派生类的指针,会自动匹配到派生类的析构函数,在调用派生类析构函数会自动调用基类的析构函数
所以要声明虚析构函数
如果将基类的析构函数声明为虚函数,那么派生类的析构函数会成为虚函数,此时编译器就会忽略指针类型,会根据指针的指向来选择函数,这样一来指针都会指向派生类的对象,就能调用派生类的构造函数,继而调用基类的析构函数,这样就能解决内存泄露的问题
虚函数和虚继承的不同
虚函数和虚继承完全是两种概念,千万不能当成同一类
1 |
|
如果我要是在Derived
类中使用seta()
函数,就会报错,因为编译器不知道m_a从何继承而来,虚继承就可以解决命名冲突的问题,让派生类中只保留一份积累的成员变量
总结一下这两者的区别
虚继承:
- 虚继承是解决C++中多继承引起的钻石问题(Diamond Problem)的一种机制。
- 当一个类通过虚继承从多个基类继承时,它只会包含一个基类的实例,避免了在对象内存模型中重复包含相同基类成员的问题。
- 虚继承在类继承列表中使用
virtual
关键字声明,指示编译器使用虚继承。
虚函数: - 虚函数是基类中声明的函数,用来允许派生类提供特定的实现。这是多态性实现的关键机制之一。
- 虚函数使得可以通过基类指针或引用调用派生类中重写的函数。
- 虚函数在基类中使用
virtual
关键字声明,在派生类中可以被重新定义