C++虚函数

本文是基于32位系统下的分析,用的vs2019
参考资料:https://blog.csdn.net/haoel/article/details/1948051/

准备工作
vs 设置查看内存分布方式:
右键-> 属性 -> C/C++ 命令行 填入 /d1 reportAllClassLayout
在这里插入图片描述
在这里插入图片描述

一般继承(无虚函数覆盖)

class A
{
public:
	virtual void f() { cout << "A::f()" << endl; }
	int _a;
};

class B : public A
{
public:
	void fb() { cout << "B::fb()" << endl; }
	int _b;
};

vs打印出的内存布局如下图
在这里插入图片描述
内容复制出来如下
1>class A size(8):
1> ±–
1> 0 | {vfptr}
1> 4 | _a
1> ±–
1>A::$vftable@:
1> | &A_meta
1> | 0
1> 0 | &A::f
1> 1 | &A::g
1> 2 | &A::h

可以看到A类有一个4字节的虚函数指针vfptr,该虚函数指针指向了一张虚函数表vftable,虚函数表中存储了每一个虚函数的地址

1>class B size(12):
1> ±–
1> 0 | ±-- (base class A)
1> 0 | | {vfptr}
1> 4 | | _a
1> | ±–
1> 8 | _b
1> ±–
1>B::$vftable@:
1> | &B_meta
1> | 0
1> 0 | &A::f
1> 1 | &A::g
1> 2 | &A::h

B类继承了A类虚函数表和成员变量

一般继承(有虚函数覆盖,子类没有自己新的虚函数)

例:在子类B中重写父类A的f()方法

class A
{
public:
	virtual void f() { cout << "A::f()" << endl; }
	virtual void g() { cout << "A::g()" << endl; }
	virtual void h() { cout << "A::h()" << endl; }
	int _a;
};

class B : public A
{
public:
	void f() { cout << "B::f()" << endl; }
	int _b;
};

1>class B size(12):
1> ±–
1> 0 | ±-- (base class A)
1> 0 | | {vfptr}
1> 4 | | _a
1> | ±–
1> 8 | _b
1> ±–
1>B::$vftable@:
1> | &B_meta
1> | 0
1> 0 | &B::f
1> 1 | &A::g
1> 2 | &A::h

可见B的虚函数表中&B::f 覆盖了原有的&A::f

一般继承(有虚函数覆盖,子类有自己新的虚函数)

例:在子类B中重写父类A的f()方法,同时新增一个自己的虚函数k()

class A
{
public:
	virtual void f() { cout << "A::f()" << endl; }
	virtual void g() { cout << "A::g()" << endl; }
	virtual void h() { cout << "A::h()" << endl; }
	int _a;
};

class B : public A
{
public:
	void f() { cout << "B::f()" << endl; }
	virtual void k() { cout << "B::k()" << endl; }
	int _b;
};

1>class B size(12):
1> ±–
1> 0 | ±-- (base class A)
1> 0 | | {vfptr}
1> 4 | | _a
1> | ±–
1> 8 | _b
1> ±–
1>B::$vftable@:
1> | &B_meta
1> | 0
1> 0 | &B::f
1> 1 | &A::g
1> 2 | &A::h
1> 3 | &B::k
可以看到新增的虚函数会顺式添加到已有的虚函数表中

多重继承(无虚函数覆盖)

class Base1
{
public:
	Base1() : _iBase1(10) {}
	virtual void f() { std::cout << "Base1::f()" << std::endl; }
	virtual void g() { std::cout << "Base1::g()" << std::endl; }
	virtual void h() { std::cout << "Base1::h()" << std::endl; }
	int _iBase1;
};

class Base2
{
public:
	Base2() : _iBase2(100) {}
	virtual void f() { std::cout << "Base2::f()" << std::endl; }
	virtual void g() { std::cout << "Base2::g()" << std::endl; }
	virtual void h() { std::cout << "Base2::h()" << std::endl; }
	int _iBase2;
};

class Base3
{
public:
	Base3() : _iBase3(1000) {}
	virtual void f() { std::cout << "Base3::f()" << std::endl; }
	virtual void g() { std::cout << "Base3::g()" << std::endl; }
	virtual void h() { std::cout << "Base3::h()" << std::endl; }
	int _iBase3;
};


class Derive
	: public Base1
	, public Base2
	, public Base3
{
public:
	Derive() : _iDerived(10000) {}
	virtual void f1() { std::cout << "Derive::f()" << std::endl; }
	virtual void g1() { std::cout << "Derive::g1()" << std::endl; }

	int _iDerived;
};

1>class Derive size(28):
1> ±–
1> 0 | ±-- (base class Base1)
1> 0 | | {vfptr}
1> 4 | | _iBase1
1> | ±–
1> 8 | ±-- (base class Base2)
1> 8 | | {vfptr}
1>12 | | _iBase2
1> | ±–
1>16 | ±-- (base class Base3)
1>16 | | {vfptr}
1>20 | | _iBase3
1> | ±–
1>24 | _iDerived
1> ±–
1>Derive::KaTeX parse error: Expected 'EOF', got '&' at position 22: …e@Base1@: 1> | &̲Derive_meta 1> …vftable@Base2@:
1> | -8
1> 0 | &Base2::f
1> 1 | &Base2::g
1> 2 | &Base2::h
1>Derive::$vftable@Base3@:
1> | -16
1> 0 | &Base3::f
1> 1 | &Base3::g
1> 2 | &Base3::h
在这里插入图片描述

总结:多重继承(无虚函数覆盖)场景
①每个父类都有自己的虚表
②派生类中的布局按照基类被声明时的顺序进行排列
③派生类自己的虚函数,会被加入到第一个基类的虚函数表中

多重继承(有虚函数覆盖)

class Base1
{
public:
	Base1() : _iBase1(10) {}
	virtual void f() { std::cout << "Base1::f()" << std::endl; }
	virtual void g() { std::cout << "Base1::g()" << std::endl; }
	virtual void h() { std::cout << "Base1::h()" << std::endl; }
	int _iBase1;
};

class Base2
{
public:
	Base2() : _iBase2(100) {}
	virtual void f() { std::cout << "Base2::f()" << std::endl; }
	virtual void g() { std::cout << "Base2::g()" << std::endl; }
	virtual void h() { std::cout << "Base2::h()" << std::endl; }
	int _iBase2;
};

class Base3
{
public:
	Base3() : _iBase3(1000) {}
	virtual void f() { std::cout << "Base3::f()" << std::endl; }
	virtual void g() { std::cout << "Base3::g()" << std::endl; }
	virtual void h() { std::cout << "Base3::h()" << std::endl; }
	int _iBase3;
};


class Derive
	: public Base1
	, public Base2
	, public Base3
{
public:
	Derive() : _iDerived(10000) {}
	void f() { std::cout << "Derive::f()" << std::endl; }
	virtual void g1() { std::cout << "Derive::g1()" << std::endl; }

	int _iDerived;
};

1>class Derive size(28):
1> ±–
1> 0 | ±-- (base class Base1)
1> 0 | | {vfptr}
1> 4 | | _iBase1
1> | ±–
1> 8 | ±-- (base class Base2)
1> 8 | | {vfptr}
1>12 | | _iBase2
1> | ±–
1>16 | ±-- (base class Base3)
1>16 | | {vfptr}
1>20 | | _iBase3
1> | ±–
1>24 | _iDerived
1> ±–
1>Derive::KaTeX parse error: Expected 'EOF', got '&' at position 22: …e@Base1@: 1> | &̲Derive_meta 1> …vftable@Base2@:
1> | -8
1> 0 | &thunk: this-=8; goto Derive::f // 这里存的不是&Derive::f,而是跳转指令
1> 1 | &Base2::g
1> 2 | &Base2::h
1>Derive::$vftable@Base3@:
1> | -16
1> 0 | &thunk: this-=16; goto Derive::f // 这里存的不是&Derive::f,而是跳转指令
1> 1 | &Base3::g
1> 2 | &Base3::h

可以看到内存分布中Derive继承了Base1 Base2 Base3的虚函数指针、虚函数表,同时按照基类Base1、Base2、Base3的声明顺序进行排列;Derive重写了f()函数,因此Base1 Base2 Base3的需表中各自的f()函数都被覆盖成&Derive::f,不过只有Base1的虚表中存的是&Derive::f,Base2 Base3虚表中都是一条跳转指令。Derive自己新增的虚函数被存放在
Base1的虚表中。

在这里插入图片描述

总结:多重继承(有虚函数覆盖)场景
①每个父类都有自己的虚表
②派生类中的布局按照基类被声明时的顺序进行排列
③派生类自己的虚函数,会被加入到第一个基类的虚函数表中
④派生类会覆盖基类的虚函数,但是只有第一个虚表中存放的是被覆盖的函数地址,其他的都存放的是跳转指令

一个简单的例子测试虚表的存在

#include <iostream>

class A
{
public:
	A() :_a(10) {}
	virtual void f() { std::cout << "A::f()" << std::endl; }
	virtual void g() { std::cout << "A::g()" << std::endl; }
	virtual void h() { std::cout << "A::h()" << std::endl; }
	int _a;
};

class B : public A
{
public:
	B() :_b(100) {}
	void f() { std::cout << "B::f()" << std::endl; }
	int _b;
};

typedef void(*Function)(void);
int main(void)
{
	B b;
#if 1
	int* vfptr = (int*)&b;
	int vtaleAdress = *vfptr;

	int* pVtable = (int*)vtaleAdress;
	int func = *pVtable;

	Function f = (Function)func;
	f();
	Function g = (Function) * (pVtable + 1);
	g();
	Function h = (Function) * (pVtable + 2);
	h();
	std::cout << "A::_a = " << (int)*(vfptr + 1) << std::endl;
	std::cout << "B::_b = " << (int)*(vfptr + 2) << std::endl;
#endif

#if 0
	int** vfptr = (int**)&b;
	for (int idx = 0; vfptr[0][idx] != NULL; ++idx) {
		Function f = (Function)vfptr[0][idx];
		f();
	}
	std::cout << "A::_a = " << (int)vfptr[1] << std::endl;
	std::cout << "B::_b = " << (int)vfptr[2] << std::endl;
#endif
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值