多态的原理剖析

多态满足条件

  • 有继承关系
  • 子类重写父类中的虚函数

多态分为两类
静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名。
动态多态: 派生类和虚函数实现运行时多态。

静态多态和动态多态区别:
静态多态的函数地址早绑定 - 编译阶段确定函数地址。
动态多态的函数地址晚绑定 - 运行阶段确定函数地址。

创建一个父类:

class Animal
{
public:
	//void speak()   不加virtual时
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
};

当不加virtual时sizeof(Animal)可以发现占用一个字节。(这是因为此时Animal类中只有一个成员函数,相当于一个空类)

为什么空类只占用一个字节?
在C++中空类会占一个字节,这是为了让对象的实例能够相互区别。具体来说,空类同样可以被实例化,并且每个实例在内存中都有独一无二的地址,因此,编译器会给空类隐含加上一个字节,这样空类实例化之后就会拥有独一无二的内存地址。如果没有这一个字节的占位,那么空类就无所谓实例化了,因为实例化的过程就是在内存中分配一块地址。
总而言之一个字节是占位用的。

为什么成员函数不占用类或者对象的空间?
成员函数可以被看作是类作用域的全局函数,不在对象分配的空间里,只有虚函数才会在类对象里有一个指针,存放虚函数的地址等相关信息。
成员函数的地址,编译期就已确定并静态绑定或动态的绑定在对应的对象上,对象调用成员函数时,编译器可以确定这些函数的地址,并通过传入this指针和其他参数,完成函数的调用,所以类中就没有必要存储成员函数的信息

在这里插入图片描述

当加上virtual时sizeof(Animal)可以发现占用四个字节。(此时内部包含一个名为vfptr(虚函数指针)的指针去指向vftable(虚函数表),表中记录父类speak函数地址)。

cl /d1 reportSingleClassLayoutAnimal "test.cpp"

在这里插入图片描述
子类继承父类:

class Cat :public Animal
{
public:
	void speak()
	{
		cout << "小猫在说话" << endl;
	}
};

class Dog :public Animal
{
public:

	void speak()
	{
		cout << "小狗在说话" << endl;
	}

};

当子类没有内容的时候(也即没有重写父类的虚函数的时候),子类会继承父类所有的内容,包括父类的虚函数表。

当子类重写父类的虚函数表时,子类中的虚函数表内部会替换成重写后的子类的虚函数地址。

cl /d1 reportSingleClassLayoutCat "test.cpp"

在这里插入图片描述
当父类的指针或引用指向子类的对象的时候,会查找对象的虚函数表,进入重写后子类虚函数表中的函数地址入口,从而就会调用子类的Cat作用范围下的speak函数地址,从而实现多态。

Cat cat;
Animal &animal = cat;
animal.speak();

此时animal指针指向的是cat的子类对象,因此去找cat的speak地址,即在运行阶段发生动态的多态。


以上整理于b站黑马程序员C++视频的某一讲。https://www.bilibili.com/video/BV1et411b73Z?p=136

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值