【C++】虚函数与虚函数表

1. 虚函数表的结构

#include <iostream>
using namespace std;

typedef void (*Fun)(void);

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

int main()
{
	Base base;
	Fun fun = nullptr;

	// &b返回Base *类型的指针
	cout << "该对象的地址:" << &base << endl;

	// (long*)(&base)将Base *类型的指针转换为long *类型的指针,转换后指针指向地址没变,但指向的对象的类型改变
	cout << "虚函数表指针:" << (long*)(&base) << endl;

	// *(long*)(&base)对long *类型的指针解引用,从该地址开始取出sizeof(long)个字节,赋值给一个long类型的对象
	cout << "虚函数表指针的地址:" << *(long*)(&base) << endl;

	// (long *)*(long *)(&b)相当于(long *)后接一个long值,返回long *,将这个long值作为该指针指向的地址值
	cout << "虚函数表指针:" << (long*)*(long*)(&base) << endl;

	// *(long *)*(long *)(&base)对long *类型的指针解引用,返回long值
	cout << "虚函数起始位置的地址:" << *(long*)*(long*)(&base) << endl;


	cout << "offset_to_top:" << *((long*)*(long*)(&base) - 2) << endl;
	cout << "typeinfo for Base:" << (long*)*((long*)*(long*)(&base) - 1) << endl;

	fun = (Fun) * ((long*)*(long*)(&base));
	cout << "虚函数表中第一个虚函数的地址:" << (long*)fun << endl;
	fun();

	fun = (Fun) * ((long*)*(long*)(&base) + 1);
	cout << "虚函数表中第二个虚函数的地址:" << (long*)fun << endl;
	fun();

	fun = (Fun) * ((long*)*(long*)(&base) + 2);
	cout << "虚函数表中第三个虚函数的地址:" << (long*)fun << endl;
	fun();

	system("pause");
	return 0;
}

详细版

简化版

2. 单继承 子类不重写父类虚函数

  • 虚函数按照其声明顺序放于表中。
  • 父类的虚函数在子类的虚函数前面。
#include <iostream>
using namespace std;

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

class Derived : public Base
{
public:
	virtual void f1() { cout << "Derived::f1()" << endl; }
	virtual void g1() { cout << "Derived::g1()" << endl; }
	virtual void h1() { cout << "Derived::h1()" << endl; }
};

简化版

3. 单继承 子类重写父类虚函数

  • 被子类重写的虚函数放到虚函数表中原来父类虚函数的位置。
  • 没有被覆盖的函数位置不变。
#include <iostream>
using namespace std;

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

class Derived : public Base
{
public:
	virtual void f() { cout << "Derived::f()" << endl; }
	virtual void g1() { cout << "Derived::g1()" << endl; }
	virtual void h1() { cout << "Derived::h1()" << endl; }
};

简化版

4. 多继承 子类不重写父类虚函数

  • 子类继承几个基类就有几个虚函数表指针。
  • 子类的特有的虚函数被放到了第一个父类的虚函数表中。
#include <iostream>
using namespace std;

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

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

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

class Derived : public Base1, public Base2, public Base3
{
public:
	virtual void f1() { cout << "Derived::f1()" << endl; }
	virtual void g1() { cout << "Derived::g1()" << endl; }
};

在这里插入图片描述

5. 多继承 子类重写父类虚函数

#include <iostream>
using namespace std;

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

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

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

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

在这里插入图片描述

总结

如果一个类中有虚函数,那么该类就有一个虚函数表。虚函数表是属于类的,所有该类的实例化对象中都有一个虚函数表指针指向该类的虚函数表。一个类只能有一个虚函数表。在编译时,一个类的虚函数表就确定了,所以它放在了只读数据段中。
在这里插入图片描述

多层继承

多层继承的原则:子类根据在其上一层父类的虚函数表基础上进行修改。

#include <iostream>
using namespace std;

class ClassA
{
public:
	// 构造函数
	ClassA() :aData(0) { cout << "ClassA::ClassA()" << endl; }
	// 析构函数
	virtual ~ClassA() { cout << "ClassA::~ClassA()" << endl; }
	// 普通成员函数
	void func1() { cout << "ClassA::func1()" << endl; }
	void func2() { cout << "ClassA::func2()" << endl; }
	// 虚函数
	virtual void vfunc1() { cout << "ClassA::vfunc1()" << endl; }
	virtual void vfunc2() { cout << "ClassA::vfunc2()" << endl; }
private:
	int aData;
};

class ClassB : public ClassA
{
public:
	// 构造函数
	ClassB() :bData(0) { cout << "ClassB::ClassB()" << endl; }
	// 析构函数
	virtual ~ClassB() { cout << "ClassB::~ClassB()" << endl; }
	// 普通成员函数
	void func1() { cout << "ClassB::func1()" << endl; }
	// 虚函数
	virtual void vfunc1() { cout << "ClassB::vfunc1()" << endl; }
private:
	int bData;
};

class ClassC : public ClassB
{
public:
	// 构造函数
	ClassC() :cData(0) { cout << "ClassC::ClassC()" << endl; }
	// 析构函数
	virtual ~ClassC() { cout << "ClassC::~ClassC()" << endl; }
	// 普通成员函数
	void func2() { cout << "ClassC::func2()" << endl; }
	// 虚函数
	virtual void vfunc2() { cout << "ClassC::vfunc2()" << endl; }
private:
	int cData;
};


void test01()
{
	ClassA* a = new ClassB;
	a->func1();		// ClassA::func1()	隐藏子类同名函数
	a->func2();		// ClassA::func2()
	a->vfunc1();	// ClassB::vfunc1()	子类重写父类虚函数
	a->vfunc2();	// ClassA::vfunc2()

	delete a;
}

void test02()
{
	ClassA* a = new ClassC;
	a->func1();          // ClassA::func1()		隐藏子类ClassB同名函数           
	a->func2();          // ClassA::func2()		隐藏孙子类ClassC同名函数
	a->vfunc1();	     // ClassB::vfunc1()	子类ClassB重写父类ClassA虚函数
	a->vfunc2();	     // ClassC::vfunc2()	孙子类ClassC重写父类ClassA虚函数

	delete a;

	ClassB* b = new ClassC;
	b->func1();				// ClassB::func1()	隐藏父类ClassA同名函数          
	b->ClassA::func1();		// ClassA::func1()	
	b->func2();				// ClassA::func2()	
	b->vfunc1();			// ClassB::vfunc1()	子类ClassB重写父类ClassA虚函数
	b->vfunc2();			// ClassC::vfunc2()	孙子类ClassC重写父类ClassA虚函数

	delete b;
}

int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

注:C++对象模型中无普通成员函数指针,图中只为了方便区分。
对于函数test01:
在这里插入图片描述
对于函数test02:
注:C++对象模型中无普通成员函数指针,图中只为了方便区分。

多继承

在多继承情况下,有多少个基类就有多少个虚函数表指针,前提是基类要有虚函数。当有多个虚函数表时,虚函数表的结果是0代表没有下一个虚函数表。*号位置在不同操作系统中实现不同,代表有下一个虚函数表。

  • 子类虚函数会覆盖每一个父类的每一个同名虚函数。
  • 子类特有的虚函数,填入第一个父类的虚函数表中,且父类指针不能调用。
  • 父类特有的虚函数,则不覆盖,子类和该父类指针都能调用。
#include <iostream>
using namespace std;

class ClassA1
{
public:
	// 构造函数
	ClassA1() :a1Data(0) { cout << "ClassA1::ClassA1()" << endl; }
	// 析构函数
	virtual ~ClassA1() { cout << "ClassA1::~ClassA1()" << endl; }
	// 普通成员函数
	void func1() { cout << "ClassA1::func1()" << endl; }
	// 虚函数
	virtual void vfunc1() { cout << "ClassA1::vfunc1()" << endl; }
	virtual void vfunc2() { cout << "ClassA1::vfunc2()" << endl; }
private:
	int a1Data;
};

class ClassA2
{
public:
	// 构造函数
	ClassA2() :a2Data(0) { cout << "ClassA2::ClassA2()" << endl; }
	// 析构函数
	virtual ~ClassA2() { cout << "ClassA2::~ClassA2()" << endl; }
	// 普通成员函数
	void func1() { cout << "ClassA2::func1()" << endl; }
	// 虚函数
	virtual void vfunc1() { cout << "ClassA2::vfunc1()" << endl; }
	virtual void vfunc2() { cout << "ClassA2::vfunc2()" << endl; }
	virtual void vfunc4() { cout << "ClassA2::vfunc4()" << endl; }
private:
	int a2Data;
};

class ClassC : public ClassA1, public ClassA2
{
public:
	// 构造函数
	ClassC() :cData(0) { cout << "ClassC::ClassC()" << endl; }
	// 析构函数
	virtual ~ClassC() { cout << "ClassC::~ClassC()" << endl; }
	// 普通成员函数
	void func1() { cout << "ClassC::func1()" << endl; }
	// 虚函数
	virtual void vfunc1() { cout << "ClassC::vfunc1()" << endl; }
	virtual void vfunc2() { cout << "ClassC::vfunc2()" << endl; }
	virtual void vfunc3() { cout << "ClassC::vfunc3()" << endl; }
private:
	int cData;
};


void test01()
{
	ClassA1* a1 = new ClassC;
	a1->func1();               // ClassA1::func1()   隐藏子类同名函数
	a1->vfunc1();              // ClassC::vfunc1()   子类重写父类虚函数
	a1->vfunc2();              // ClassC::vfunc2()   子类重写父类虚函数
	//a1->vfunc3()	//  子类特有的虚函数,父类指针不能调用

	delete a1;

	ClassA2* a2 = new ClassC;
	a2->func1();               // ClassA2::func1()   隐藏子类同名函数
	a2->vfunc1();              // ClassC::vfunc1()   子类重写父类虚函数
	a2->vfunc2();              // ClassC::vfunc2()   子类重写父类虚函数
	a2->vfunc4();              // ClassA2::vfunc4()  未被子类重写的父类虚函数

	delete a2;

	ClassC* c = new ClassC;
	c->func1();				// ClassC::func1()	隐藏父类同名函数
	c->ClassA1::func1();	// ClassA1::func1()
	c->ClassA2::func1();	// ClassA2::func1()

	c->vfunc1();			// ClassC::vfunc1()
	c->vfunc2();            // ClassC::vfunc2()
	c->vfunc3();            // ClassC::vfunc3()
	c->vfunc4();            // ClassA2::func4()	父类特有的虚函数,子类也可以调用

	delete c;
}

int main()
{
	test01();
	system("pause");
	return 0;
}

注:C++对象模型中无普通成员函数指针,图中只为了方便区分。
注:C++对象模型中无普通成员函数指针,图中只为了方便区分。

参考:https://blog.csdn.net/weixin_43798887/article/details/118196343
参考:https://blog.csdn.net/haoel/article/details/1948051
参考:https://blog.csdn.net/qq_36359022/article/details/81870219

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值