11. 虚函数

多态:不关心子类对象的具体类型,调用子类对象自己的虚函数。

实现:虚函数。通过父类的指针或引用调用虚函数时才发生动态绑定。

虚表:虚函数构成的数组。

虚表指针:指向数组的指针

虚表指针的赋值时机:每个类在构造和析构时,会把虚表指针填成自己虚表的地址。子类构造时,先构造父类,于是先赋值为父类的虚表指针,父类构造完毕后,再赋值为子类的虚表指针。

虚表的创建在编译期就完成了,数据保存在exe中。

简单举例:

 

class B {
public:
	B() {

	}
	virtual void f1() {}
	virtual void f2() {}
	virtual void f3() {}
	virtual void f4() {}
};

class D :public B {
public:
	D() {

	}
	virtual void f1() override {}
	virtual void f2() override {}
};

 

  

 

010 Editor打开磁盘中的exe,选中的一行是虚表,有四个虚函数:

 

 

 

vs2019调试状态:

 

 

 

内存情况: 

 

 

 

 

 

整个过程:

一.编译期:

1.编译器创建父类B的虚表

 

B::vftable[4] = {&B::f1, &B::f2, &B::f3, &B::f4};

 

  

2.编译器拷贝父类B的虚表给子类

 

D::vftable[4] = {&B::f1, &B::f2, &B::f3, &B::f4};

 

  

3.编译器替换子类D重写的部分,未重写则继续用父类的

 

 

 

D::vftable[4] = {&D::f1, &D::f2, &B::f3, &B::f4};

  

 

二.运行期:

书写B *p = new D;时,发生如下事情:

1.构造父类B

__vfptr = B::vftable;

  

2.构造子类D

__vfptr = D::vftable;

  

调用虚函数p->f1();时,通过虚表指针,以数组下标寻址的方式获取数组第一项,即虚函数f1的地址,然后调用:

(p->(__vfptr[0])) ();

  

 

 

多态在父类成员函数中的表现:

1.在父类的一般成员函数中调用虚函数,有多态效果.

2.在父类的构造函数中调用虚函数,此时虚表指针指向父类自己的虚表,子类尚未初始化,调用子类虚函数可能崩溃,vs2019直接以静态绑定方式调用。

3.在父类的析构函数中调用虚函数,此时虚表指针指向父类自己的虚表,子类早已经析构,调用子类虚函数可能崩溃,vs2019直接以静态绑定方式调用。

 

构造函数不允许标记为virtual,父类的虚构函数一般都标记为virtual

 

class B {
public:
	B() {
		f();//构造时,虚表指针指向自己的虚表,调用B::f,观察反汇编发现以静态绑定方式调用B::f
	}
	void foo() {
		f();
	}
	~B() {
		f();//析构时,虚表指针指向自己的虚表,调用B::f,观察反汇编发现以静态绑定方式调用B::f
		cout << "~B" << endl;
	}
	virtual void f() {

		cout << "~Bf" << endl;
	}
};

class D :public B {
public:
	D() {
		f();//构造时,虚表指针指向自己的虚表,调用D::f,观察反汇编发现以静态绑定方式调用D::f
	}
	~D() {
		f();//析构时,虚表指针指向自己的虚表,调用D::f,观察反汇编发现以静态绑定方式调用D::f
		cout << "~D" << endl;
	}
	virtual void f() override {
		cout << "~Df" << endl;
	}
};


int  main() {

	B *p = new D;
	p->foo();//foo内部调用f时发生动态绑定,此时虚表指针指向子类D的虚表,调用D的虚函数f
	cout << endl;

	//由于基类B的析构函数是virtual,所以子类D的析构函数自动成为虚函数,即使~D没加virtual也如此,
	//所以delete p时,先调用子类D的析构函数,再调用B的析构函数。
	//如果基类B的析构函数未标记为virtual,则delete p只调用基类B的析构函数。
	delete p;
	
	return 0;
}

 

  

 

 

posted @ 2020-05-20 07:55  八转达人  阅读(268)  评论(0)    收藏  举报