C++学习总结(一)虚函数

前言:工作了小半年了,准备开个系列的总结笔记,主要从虚函数、const、回调函数、类的构建与析构特点、结构体中指针构造等方面出发,慢慢总结进步~

虚函数

    先贴上自己学习理解参考的链接

ref1:https://blog.csdn.net/haoel/article/details/1948051 虚函数

ref2:  https://blog.csdn.net/weixin_43329614/article/details/89103574 虚函数

ref3:  https://blog.csdn.net/tonychan129/article/details/21294879  模板技术

    C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术(编译时决议),RTTI技术(运行时决议),虚函数技术(运行时决议),要么是试图做到在编译时决议,要么试图做到运行时决议。

    虚函数的实现是由两个部分组成的,虚函数指针与虚函数表。虚函数继承时必须保持与父类一样的类型和参数以及修饰符【const情况下,如果子类没有加const,则算是重载】

1. 虚函数指针和虚函数表

    在一个实例化的类的对象中,如果存在虚函数,则在该对象的地址首位后,紧跟虚函数表指针,这个表内存放了该类所有的虚函数指针。

假设有一个类Base,里面有如下的虚函数:

class Base
{
	public:
		virtual void f() { ; }
		virtual void g() { ; }
		virtual void h() { ; }

};

若实例化一个对象b, 则b的地址和内容对应如下【虚函数后面有个点,代表虚函数的结束符,类似‘\0’】:

此外我们可以通过如下代码来访问具体虚函数:我们可以通过强行把&b转成int *,取得虚函数表的地址,然后,再次取址就可以得到第一个虚函数的地址了,也就是Base::f(),这在上面的程序中得到了验证(把int* 强制转成了函数指针)

typedef void(*Fun)(void);

int main()
{	
	Base b;
	Fun pFun = NULL;
	
	//访问第一个虚函数
	cout << "虚函数表地址:" << (int*)(&b) << endl;
	cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;

	pFun = (Fun) * ((int*)*(int*)(&b));
	pFun();

	//访问其他虚函数
	cout << "虚函数表 — 第二个函数地址:" << (int*)*(int*)(&b) + 1<< endl;
	pFun = (Fun) * ((int*)*(int*)(&b) + 1);
	pFun();

	return 0;
}

输出如下【顺带可以看到指针值加了4(int型指针)】:

 

  


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

假设有一个类Derived继承Base,具体实现如下:

class Derived:public Base
{
public:
	virtual void f1() { ; }
	virtual void g1() { ; }
	virtual void h1() { ; }

};

假设有如上所示的继承关系,类Derive继承了类Base,并且未重载了Base的任何函数,则类Derived的虚函数表如下【继承类的虚函数在基类的后面按声明依次排序】:

通过如下代码打印虚函数表来验证:

// 表示用户自己定义了一个函数指针数据类型
//typedef  返回类型(*新类型)(参数表)
typedef void(*Fun)(void);

int main()
{	
	Derived b;
	Fun pFun = NULL;
	// 表示定义了一个函数指针pFun ,改函数指针指向类似于void *pFun(void)的函数

	
	//访问第一个虚函数
	cout << "虚函数表地址:" << (int*)(&b) << endl;
	cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;


	//&b取地址,int*将其转换成整型指针,*取对应内容,将内容转换成整型指针,最后转换成Fun类型指针,Fun
	pFun = (Fun) * ((int*)*(int*)(&b));
	pFun();

	//访问其他虚函数
	cout << "虚函数表 — 第二个函数地址:" << (int*)*(int*)(&b) + 1<< endl;
	pFun = (Fun) * ((int*)*(int*)(&b) + 1);
	pFun();

	//访问其他虚函数
	cout << "虚函数表 — 第四个函数地址:" << (int*)*(int*)(&b) + 3 << endl;
	pFun = (Fun) * ((int*)*(int*)(&b) + 3);
	pFun();


	return 0;
}


3.一般继承(有虚函数覆盖)

假设有一个类Derived继承Base,具体实现如下:

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

};

即:

此时函数的虚函数表如下:

通过如下代码打印虚函数表来验证,可以看出1.覆盖的f函数被放到了虚表中原来父类虚函数的位置。  2.没有被覆盖的函数依旧】

 

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

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; }
	//virtual void h1() { cout << "Derived::h1" << endl; }

};

即:

此时虚函数表如下,

通过如下代码打印虚函数表来验证,


// 表示用户自己定义了一个函数指针数据类型
//typedef  返回类型(*新类型)(参数表)
typedef void(*Fun)(void);

int main()
{	
	Derived b;
	Fun pFun = NULL;
	// 表示定义了一个函数指针pFun ,改函数指针指向类似于void *pFun(void)的函数

	
	//访问第一个虚函数
	cout << "虚函数表地址:" << (int*)(&b) << endl;
	cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;


	//&b取地址,int*将其转换成整型指针,*取对应内容,将内容转换成整型指针,最后转换成Fun类型指针,Fun
	pFun = (Fun) * ((int*)*(int*)(&b));
	pFun();

	//访问其他虚函数
	cout << "虚函数表 — 第二个函数地址:" << (int*)*(int*)(&b) + 1<< endl;
	pFun = (Fun) * ((int*)*(int*)(&b) + 1);
	pFun();

	//访问其他虚函数
	cout << "虚函数表 — 第四个函数地址:" << (int*)*(int*)(&b) + 3 << endl;
	pFun = (Fun) * ((int*)*(int*)(&b) + 3);
	pFun();

	//访问第二个虚函数表
	int* tmp = (int*)(&b) + 1;
	cout << "虚函数表地址:" << tmp << endl;
	cout << "NO2.虚函数表 — 第一个函数地址:" << (int*)*(tmp) << endl;
	pFun = (Fun) * ((int*)*(tmp));
	pFun();

	//访问第三个虚函数表
	int* tmp2 = (int*)(&b) + 2;
	cout << "虚函数表地址:" << tmp2 << endl;
	cout << "NO3.虚函数表 — 第一个函数地址:" << (int*)*(tmp2) << endl;
	pFun = (Fun) * ((int*)*(tmp2));
	pFun();



	return 0;
}

 可以看出,和虚函数表对应起来了

  


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

继承关系如图所示

此时虚函数表如下:

经过验证,可以看到对应虚函数都上所示被正确覆盖

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值