虚函数相关讨论

来源:互联网 发布:韩国东大门知乎 编辑:程序博客网 时间:2024/05/21 08:54
下列关于虚函数相关说法正确的有?

(1) 多态是通过虚表实现的 (√)
(2) 构造函数可以声明为虚函数 (x)
(3) 析构函数不可以声明为虚函数 (x)
(4) 抽象类中至少包含一个纯虚函数 (√)

C++中 的虚函数的作用主要是实现了多态的机制。而虚函数是通过虚函数表(V-Table)实现的。

构造函数为什么不能声明为虚函数?
1 构造一个对象的时候,必须知道对象的实际类型。如果构造函数是虚函数,虚函数行为在运行期间才能确定实际类型。对象还未构造成功,编译器无法知道对象的实际类型,所以构造函数不可以为虚函数。
2 虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初始化,将无法进行。

析构函数为什么声明为虚函数?
如果析构函数不是虚函数,而程序执行时又要通过基类的指针去销毁派生类的动态对象,那么用delete销毁对象时,只调用了基类的析构函数,未调用派生类的析构函数。这样会造成销毁对象不完全。

顺序:
构造函数执行时先调用基类的构造函数,其次才调用派生类的构造函数。
析构函数执行时先调用派生类的析构函数,其次才调用基类的析构函数。

包含至少一个纯虚函数的类视为抽象类

虚函数之于析构函数的讨论

1

#include<stdio.h>class A{    public:    A(){ printf("A");}    ~A(){ printf("~A");}};class B: public A{    public:        B(){ printf("B");}        ~B(){ printf("~B");}};int main(){    A* c = new B[2];    delete[] c;    return 0;}

结果:ABAB~A~A

1 声明的是基类的指针,基类的析构函数不是虚函数,不会调用派生类的析构方法。

2

#include<stdio.h>class A{    public:    A(){ printf("A");}    virtual ~A(){ printf("~A");}};class B: public A{    public:        B(){ printf("B");}        ~B(){ printf("~B");}};int main(){    A* c = new B[2];    delete[] c;    return 0;}

结果:ABAB~B~A~B~A

2 当基类的析构函数是虚函数时,基类指针指向派生类对象的空间被释放时会自动调用派生类的析构函数。

3

#include<stdio.h>class A{    public:    A(){ printf("A");}    ~A(){ printf("~A");}};class B: public A{    public:        B(){ printf("B");}        ~B(){ printf("~B");}};int main(){    B* c = new B[2];    delete[] c;    return 0;}

结果:ABAB~B~A~B~A

4

#include<stdio.h>class A{    public:    A(){ printf("A");}    virtual ~A(){ printf("~A");}};class B: public A{    public:        B(){ printf("B");}        ~B(){ printf("~B");}};int main(){    B* c = new B[2];    delete[] c;    return 0;}

结果:ABAB~B~A~B~A

由程序3、4可知,不管基类析构函数加不加virtual,只要是声明的是派生类的指针,并且实际指向的实例是派生类的,会先调用派生类的析构方法,然后级联调用父类的析构方法。

1 vs 2小结
在C++中,析构函数的作用是:当一个对象被销毁时,调用析构函数对类对象和对象成员进行释放内存资源。
当我们定义一个指向派生类类型对象指针时,构造函数按照从基类到派生类的顺序被调用,但是当删除指向派生类的基类指针时,派生类的析构函数没有被调用,只是调用了基类的析构函数,此时派生类将会导致内存泄漏
我们需要将基类的析构函数声明为虚函数,此时在调用析构函数的时候是根据ptr指向的具体类型来调用析构函数,此时会调用派生类的析构函数。