虚函数的作用和实现原理
1. virtual虚函数作用
- 实现C++三大特性之一的多态
- 当父类的某个函数,需要在子类中重写时,就在父类中定义为虚函数;
- 这样当定义一个父类指针指向子类时,就能动态地调用某子类的(被重写)函数;
纯虚函数: 父类中没有定义,而在子类中需要用到,就定义为:virtual void f() = 0;
class A {
public:
virtual void func() = 0; // 这种写法叫纯虚函数
};
class B: public A {
public:
void func() {
cout << "this is B" << endl;
}
};
int main()
{
A *p = new B;
p->func();
return 0;
}
打印结果为:
this is B
2. 虚函数原理–虚函数表(virtual table)
- 父类的实例化存储了一个虚函数指针(v_ptr),它指向了一张虚函数表(v_table),这张表记录了到每个子类的对应关系;当通过父类指针调用子类方法时,就会调用v_ptr和v_table;
3. 构造函数与折构函数能否申明为虚函数?
- 场景1: B *p = new B; (B是A的一个子类, 如下代码)
- 首先明确调用顺序:
- 构造函数:先调用父类的,再调用子类的;不可以为虚函数
- 折构函数:先调用子类的,再调用父类的;可以是虚函数
- 调用构造函数时,该子类的内存空间还未分配,父类的虚函数表没有任何意义;
class A {
public:
A() {cout << "A()" << endl;}
~A() {cout << "~A()" << endl;}
};
class B: public A {
public:
B() {cout << "B()" << endl;}
~B() {cout << "~B()" << endl;}
};
int main()
{
B *p = new B;
delete p;
return 0;
}
执行结果:
/Users/mac/CLionProjects/testC++/cmake-build-debug/testC_
A()
B()
~B()
~A()
- 场景2:父类指针指向子类: A *p = new B;
- 若A的折构函数不是虚函数:
- delete p时,只释放了A的内存,会造成子类B的内存部分泄漏;
- 若A的折构函数是虚函是:
- 将先通过A的虚函数表找到B的折构函数,从而先释放B的内存,最后释放A;
- 因此在这种场景下,推荐将父类的折构函数申明为虚函数