栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++40个入门知识点

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++40个入门知识点

接上文,本文从内存角度理解虚函数。
(1)首先看一下virtual加和不加对内存大小的影响:
不加virtual

 class CPerson 
{ 
...
	 void speak() {
		//printf("说人话rn");
...
};

三个类的大小:

加virtual

 class CPerson 
{ 
...
	 virtual void speak() {
		//printf("说人话rn");
...
};

三个类的大小:

(2)为什么加了virtual会比不加virtual大了四个字节呢?
先看看加了virtual之后多了什么东西,从下图可以看出chs对象中多出了一个地址,并且地址里面还包含一个地址,看起来像是指向函数的,是不是这样呢?

为了查看指定内存的内容,只需要像下图将相应的地址拖入左侧的内从中即可,既可以改变内存地址,又可以改变内容

为了探究这个问题,我们需要从汇编的角度去看,但怎么去查看呢?查看的方法如下


设置好断点之后,在汇编界面即可看到相应地址内容,第一个地址没对上,还没弄懂,但是函数的地址是与我们在“监视“中看到的是一致的。

F11之后就会跳入指向的函数

再找一个对象看看,发现两个指向函数的地址都是一样的,这个时候是不是感觉到虚函数作用的原理了?
简单一些总结就是:间接调用

3.虚函数原理总结
//虚函数的原理
//1.虚函数的调用方法是间接调用(先查虚表地址,再查虚表中的虚函数指针)
//2.增加了虚函数virtual关键字的对象头部4个字节是虚表地址(某些情况:单继承)

原理图如下:

4.模拟上述调用过程的

5.学习视频地址:深入理解虚函数的原理
6.学习笔记:

#include 

//面向对象:多态
//虚函数的原理
//1.虚函数的调用方法是间接调用(先查虚表地址,再查虚表中的虚函数指针)
//2.增加了虚函数virtual关键字的对象头部4个字节是虚表地址(某些情况:单继承)

 class CPerson 
{ 
public:
	CPerson() {
		m_nType = 0;
	}
	//虚函数
	//多态:某一个函数在父类子类有不同的实现,运行时对象自行决定调用哪一个类下实现
	 virtual void speak() {
		//printf("说人话rn");
	}

	int m_nType;
};

class CChinese :public CPerson {
public:
	CChinese() {
		m_nType = 1;
	}
	void speak() {
		printf("说中文rn");
	}
};

class CEnglish :public CPerson {
public:
	CEnglish() {
		m_nType = 2;
	}

	void speak() {
		printf("说英语rn");
	}
};

//定义一个类的成员函数指针的类型
typedef void(CPerson::*PFN_SPEAK)();
union MyFuncAddr {
	unsigned int n;
	PFN_SPEAK pfn;

};

int main(int argc,char* argv[])
{
	CChinese chs;

	CChinese chs2;

	CEnglish eng;
	//eng.speak();
	chs.speak();

	int nPersonsize = sizeof(CPerson);
	int nChinesesize = sizeof(CChinese);
	int nEnglishsize = sizeof(CEnglish);

	//一群人,每个人说自己的语言
	//CPerson ary[3];//只能代表有三个人
	CPerson* ary[3]; //用父类指针指向子类对象

	//把子类指针强转为父类,父类的三个指针调用的都是父类的
	ary[0] = &chs;
	ary[1] = ŋ
	ary[2] = &chs2;

	//模拟每个人说话
	for (int i = 0; i < 3; i++)
	{
		printf("%d:",i+1);
		ary[i]->speak();//间接调用

		//猜测编译器的工作过程
		//void* pObjtAddr = ary[0];//获取对象地址,代表对象的地址0x00bcfba0
		//unsigned int pAddr = *(unsigned int*)pObjtAddr;//获取对象首四字节的地址
		//MyFuncAddr nFunAddr;
		//nFunAddr.n = *(unsigned int*)pAddr;//获取对象首四字节所指向的函数指针的地址
		//(ary[i]->*nFunAddr.pfn)();

		//int n = 1;
		//virtual达到的效果
		//if (ary[i]->m_nType == 1)
		//{
		//	//表示该对象是中国人
		//	//父类转子类后再子类转父类是可以强制转换的
		//	CChinese* pChs = (CChinese*)ary[i];
		//	pChs->speak();
		//}
		//else if (ary[i]->m_nType == 2)
		//{
		//	//表示该对象是英国人
		//	//父类转子类后再子类转父类是可以强制转换的
		//	CEnglish* pEng = (CEnglish*)ary[i];
		//	pEng->speak();
		//}

		//ary[i]->speak();
	}

	return 0;
}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/384524.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号