Cpp多态的实现

一.炼气期

1.1 多态与虚函数;

人们希望 cpp 存在一种抽象的引用(基类指针),跟随引用对象的类型变化,被引用对象的行为也不同;

#include <iostream>
using namespace std;

//父类
class Father1 {
public:
	Father1() { 
		value = 10; 
		cout<<"father create \n"<<endl; 
	}
	
	void speak() {
	       	cout << "Father1::speak \n" << endl; 
	}
	
	void run() {
		cout << "Father1::run \n" << endl;
	}
	
	~Father1(){ 
		cout << "Father1::~Father1 \n" << endl; 
	}

	int value;
};

//子类
class SonObject :public Father1
{
public:
	SonObject(){
		cout << "son create" << endl;
	}
	
	~SonObject() {
		cout << "SonObject::~SonObject" << endl;
	}
 
    void speak() { // 注意 没有用 virtual 修饰 speak // 是重写
	       	cout << "son::speak" << endl; 
	}
	
    void run() {
		cout << "son::run" << endl;
	}
};

int main()
{
	SonObject s;
	Father1  f1;
	Father1 *f2 = static_cast<Father1 *>(&s); 
  
  	s.speak();
  	f1.speak();
  	f2->speak();
  	
	return 0;
}

运行结果:

father create  	# s 中父类构造
son create		# s 中子类构造
father create	# f1 构造
son::speak		# s.speak();
Father1::speak	# f1.speak();
Father1::speak	# f1.speak(); 不是虚函数, 没有多态
Father1::~Father1 # f1 析构
SonObject::~SonObject # s 析构
Father1::~Father1

将 speak 声明为虚函数后

#include <iostream>
using namespace std;

//父类
class Father1 {
public:
	//... 其他成员 ...
	virtual void speak() {
	       	cout << "Father1::speak \n" << endl; 
	}
	//... 其他成员 ...
};

//子类
class SonObject :public Father1
{
public:
	//... 其他成员 ...
  	void speak() {
	       	cout << "son::speak \n" << endl; 
	}
	//... 其他成员 ...
};

int main()
{
	SonObject s;
  	Father1 *f2 = static_cast<Father1 *>(&s);
  	f2->speak();
  	f2->run();
  	
	return 0;
}

运行结果:

father create
son create
son::speak		# speak 是虚函数,有多态
Father1::run	# run 没有多态,所以等价于 直接调用父类成员函数
SonObject::~SonObject
Father1::~Father1

在这里插入图片描述

1.2 基类 析构函数 最好是虚函数?

由上可知,如果 基类 析构函数 不是虚函数:

//父类
class Father1;
//子类
class SonObject :public Father1
{
	void *pSonMem;
public:
	SonObject() { // 比如 构造函数中 malloc 申请了内存
		pSonMem = malloc(1);
	}
	~SonObject() { // 子类 希望在析构时释放 pSonMem 引用的内存
		free(pSonMem);
	}
	//... 其他成员 ...
};
int main()
{
  Father1 *f2 = static_cast<Father1 *>(new SonObject());
  f2->speak();
  f2->run();
  delete(f2);
  return 0;
}

运行结果:没有调用子类的析构函数,子类中 pSonMem 发生泄漏 !

father create
son create
son::speak
Father1::run
Father1::~Father1 # 只调用了父类的析构函数,没有多态的悲剧

【Tips】使用 override 可以检测出代码中意外的继承行为

class BaseClass{
 virtual void funcA();
 void funcD();
};
class DerivedClass: public BaseClass
{
virtual void funcA(); // ok, works as intended
void funcD() override; 
};

编译报错 compiler error: funcD is nor exist or a virtual function
因为 BaseClass 的设计者就不希望 funcD 被重写
而 BaseClass 的使用者养成 override 的好习惯后就能避免 错误的 重写 (override)

二.筑基期

2.1 虚函数内存模型;

#include <iostream>
using namespace std;
//父类
class Father1 {
public:
	Father1() { 
		value = 10; 
		cout<<"father create \n"<<endl; 
	}
	
	virtual void speak() {
	       	cout << "Father1::speak \n" << endl; 
	}
	
	virtual void run() {
		cout << "Father1::run \n" << endl;
	}
	
	~Father1(){ 
		cout << "Father1::~Father1 \n" << endl; 
	}

	int value;
};
//子类
class SonObject :public Father1
{
public:
	SonObject(){
		value = 10; 
		cout << "son create \n" << endl;
	}
	
	~SonObject() {
		cout << "SonObject::~SonObject \n" << endl;
	}
  
  void speak() {    // 子类 仅对 speak 进行重写
		cout << "son :: speak \n" << endl;
	}
};

int main()
{
  SonObject s;
  Father1  f1;
  Father1  f2;
	
	cout  	<< "f1 addr " << ((int*)&f1) << endl                      	// 父类 起始地址
		    << "vtb addr :" << *(int **)(&f1) << endl                	// 父类 虚函数表(struct)指针
		    << "vtb func0 addr " << *(*(int ***)(&f1)) << endl       	// 父类 虚函数表 虚函数0 入口 = Father1::speak 
		    << "vtb func1 addr " << *(*(int ***)(&f1)+1) << "\n\n";  	// 父类 虚函数表 虚函数1 入口 = Father1::run
  
  	cout  	<< "f2 addr " << ((int*)&f2) << endl                      	// 父类 起始地址
		    << "vtb addr :" << *(int **)(&f2) << endl                	// 父类 虚函数表(struct)指针
		    << "vtb func0 addr " << *(*(int ***)(&f2)) << endl       	// 父类 虚函数表 虚函数0 入口 = Father1::speak
		    << "vtb func1 addr " << *(*(int ***)(&f2)+1) << "\n\n";  	// 父类 虚函数表 虚函数1 入口 = Father1::run
            
	cout 	<< "s addr " << ((int*)&s) << endl                       	// 子类 起始地址
	      	<< "&s.value : " << &s.value << endl                     	// 子类 唯一数据成员地址
		    << "vtb addr :" << *(int **)(&s) << endl                 	// 子类 虚函数表(结构体)指针
		    << "vtb func0 addr " << *(*(int ***)(&s)) << endl        	// 子类 虚函数表 虚函数0 入口 = SonObject::speak 
		    << "vtb func1 addr " << *(*(int ***)(&s)+1) << "\n\n";   	// 子类 虚函数表 虚函数1 入口 = SonObject::run

	return 0;
}

运行结果

father create 
son create # s 构造完成

father create # f1 构造完成
father create # f2 构造完成

f1 addr 0xffffcf587528			# 父类 起始地址
vtb addr :0xaaaad0bcfce0		# 父类 虚函数表地址
vtb func0 addr 0xaaaad0bb122c	# 父类 speak 入口
vtb func1 addr 0xaaaad0bb1264	# 父类 run 入口

f2 addr 0xffffcf587538			# 父类 起始地址
vtb addr :0xaaaad0bcfce0		# 父类 虚函数表地址 f2 与 f1 相同
vtb func0 addr 0xaaaad0bb122c	# 父类 speak 入口
vtb func1 addr 0xaaaad0bb1264	# 父类 run 入口

s addr 0xffffcf587518			# 子类 起始地址
&s.value : 0xffffcf587520		# 子类 数据成员(地址)排在虚函数表指针之后
vtb addr :0xaaaad0bcfcc0		# 子类 虚函数表地址,不同于父类
vtb func0 addr 0xaaaad0bb13ac	# 子类::speak 与 父类::speak  不相同
vtb func1 addr 0xaaaad0bb1264	# 子类::run 与 父类::run 相同

Father1::~Father1	# f2 析构

Father1::~Father1	# f1 析构

SonObject::~SonObject	
Father1::~Father1 # f2 析构

2.2 构造函数不能是 虚函数 !

虚函数表 是在 构造对象时分配内存后 才存在,如果 构造函数 也是虚函数,那么没有表(对应的那块内存),也就没有 虚函数?
先有鸡 还是先有蛋?
先有 “蛋”!对象被构造完了,才能从"蛋"里孵化出第一只鸡(虚函数表)!

三. 结丹期

3.1 多重继承

#include <iostream>
using namespace std;

//父类1
class Father1 {
public:
	Father1() { 
		value = 10; 
		cout<<"father1 create"<<endl; 
	}
	
	virtual void speak() {
	       	cout << "Father1::speak" << endl; 
	}
	
	~Father1(){ 
		cout << "Father1::~Father1" << endl; 
	}

	int value;
};

//父类2
class Father2 {
public:
	Father2() { 
   		value2 = 1;
		cout<<"father2 create"<<endl; 
	}
	
	// 如果再声明 virtual void speak() 编译器将报错:request for member ‘speak’ is ambiguous
	void speak() {
		cout << "Father2::speak" << endl;
	}
	
	virtual void run() {
		cout << "Father2::run" << endl;
	}
	
	virtual void eat() {
		cout << "Father2::eat" << endl;
	}
	
	~Father2(){ 
		cout << "Father2::~Father2" << endl; 
	}

	// int value; 与 Father1 成员重名,compiler error: request for member ‘value’ is ambiguous
   int value2;
};

//子类 多重继承
class SonObject:public Father1,public Father2{
public:
	virtual void speak() override {
	       	cout << "SonObject::speak" << endl; 
	}
};

int main()
{
  	SonObject s;
	Father1  f1;
	Father2  f2;
  	Father1 *f1p = static_cast<Father1 *>(&s);
	Father2 *f2p = static_cast<Father2 *>(&s);
 	
 	cout 	<< "f1 addr " << ((int*)&f1) << endl 
			<< "vtb addr :" << *(int **)(&f1) << endl 
			<< "vtb func0 addr " << *(*(int ***)(&f1)) << "\n---------\n";
	
	cout 	<< "f2 addr " << ((int*)&f2) << endl 
			<< "vtb addr :" << *(int **)(&f2) << endl 
			<< "vtb func0 addr " << *(*(int ***)(&f2)) << endl 
			<< "vtb func1 addr " << *((*(int ***)(&f2))+1) << "\n---------\n";
			
	cout 	<< "s addr " << ((int*)&s) << endl 
			<< "f1p :" << f1p << " sizeof(f1p) = " << sizeof(f1p) << endl 
    		<< "vtb1 addr :" << *(int **)(&s) << endl 
    		<< "father1 value addr :" << (int*)&s + 2 << " value :" << *((int*)&s + 2) << endl
    		<< "s.father1.vtb func0 addr " << *(*(int ***)(&s)) << endl
    		<< "f2p :" << f2p << endl
    		<< "vtb2 addr :" << *(int **)((Father1*)(&s) + 1) << endl //指针+1 => s的起始地址 向后偏移一个 Father1类 的大小
			<< "s.father2.vtb func0 addr " << *(*(int ***)((Father1*)(&s) + 1)) << endl
			<< "s.father2.vtb func1 addr " << *((*(int ***)((Father1*)(&s) + 1))+1) << "\n---------\n";
			
   cout 	<< "&s.value : " << &s.value << endl
    		<< "&(f1p->value) : " << &(f1p->value) << endl
    		<< "&(s.value2) : " << &s.value2 << endl
    		<< "&(f2p->value2) : " << &(f2p->value2) << "\n---------\n";
    		
   s.speak();
   s.run();
   f1p->speak();  // s 对 speak 的多态
   f2p->speak();  // s 从 Father2 继承的 speak
   
   return 0;
}

结果:

father1 create	
father2 create	# s 构造完成
father1 create	# f1 构造完成
father2 create  # f2 构造完成

f1 addr 0xffffcb3da5c8
vtb addr :0xaaaaac71fca8 # f1 虚函数表地址
vtb func0 addr 0xaaaaac70155c # Father1::speak
---------
f2 addr 0xffffcb3da5d8
vtb addr :0xaaaaac71fc88 # f2 虚函数表地址
vtb func0 addr 0xaaaaac701668	# Father2::run
vtb func1 addr 0xaaaaac7016a0	# Father2::eat
---------
s addr 0xffffcb3da5e8
f1p :0xffffcb3da5e8 sizeof(f1p) = 8 # 子类 Father1 虚函数表指针 存放的地址,即 this,一个虚函数表指针的大小 = 8字节
vtb1 addr :0xaaaaac71fc50			# 子类 Father1 虚函数表地址
father1 value addr :0xffffcb3da5f0 value :10	# 即 上述f1p + 0x08,紧贴在 子类 Father1 虚函数表指针 后面
s.father1.vtb func0 addr 0xaaaaac701720			# SonObject::speak 的入口 重写后 与 father1 不同
f2p :0xffffcb3da5f8					# 子类 Father2 虚函数表指针 存放的地址, 即 s 的起始地址 向后偏移 sizeof(Father1)
vtb2 addr :0xaaaaac71fc68			# 子类 Father2 虚函数表地址
s.father2.vtb func0 addr 0xaaaaac701668			# SonObject::run, 与 Father2::run 相同
s.father2.vtb func1 addr 0xaaaaac7016a0			# SonObject::eat, 与 Father2::eat 相同
---------
&s.value : 0xffffcb3da5f0
&(f1p->value) : 0xffffcb3da5f0
&(s.value2) : 0xffffcb3da600
&(f2p->value2) : 0xffffcb3da600
--------- # 多重继承内存模型,等价于 子类 按继承列表的顺序 依次拼接父类的内存模型;

SonObject::speak	# s.speak();
Father2::run		# s.run();
SonObject::speak	# f1p->speak(); 多态
Father2::speak		# f2p->speak(); 纯继承, Father2 中的 speak 不是虚函数,所以也不存在冲突
  
Father2::~Father2	# f2 析构完成
Father1::~Father1	# f1 析构完成
Father2::~Father2	
Father1::~Father1	# s 析构完成

结论:

  1. 多重继承构造顺序 与 继承列表的顺序 相同;
  2. 多重继承内存模型,等价于 子类 按继承列表的顺序 依次拼接父类的内存模型;
  3. 如果父类 同名函数 不是虚函数,会被视作普通成员函数继承下来,不存在子对象的内存模型中;(上述 Father2::speak)

在这里插入图片描述

四. 元婴期

4.1 抽象类

在很多情况下,基类本身只是抽象概念,并没有实体(例如,“动物” 就只是一个抽象,现实世界中只有具体的 “猫“、”狗“)。
抽象类 应需求而生:包含纯虚函数的类称为抽象类,纯虚函数没有函数的实现,所以抽象类不能实例化。
抽象类 主要作为派生类提供一个公共的接口,派生类中对纯虚函数进行实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值