c++学习记录多态

多态分为静态多态和动态多态

静态多态为函数重载运算符重载,动态多态为派生类与虚基类运行时的多态

动态多态属于地址晚绑定,即编译阶段不知道调用哪个派生类对虚函数的重写

静态多态则是地址早绑定,编译阶段通过函数参数类型,参数数量不同编译器能自动绑定函数运行关系。

多态实现原理

class base
{
public:
	virtual void func()
	{
		;
	}
};

加入关键字 virtual,此时运行sizeof发现该类不为空,实际上是多了一个虚函数指针,虚函数指针指向虚函数列表,用于指示执行时运行哪个函数,当创建一个派生类并重写虚函数时,此时派生类的虚函数指针指向重写的函数,再将基类指针指向派生类(c++允许直接取等),此时基类中的虚函数指针是被派生类中的虚函数指针赋值的,调用基类虚函数,由于虚函数指针是派生类赋值而来,此时基类的虚函数指针指向派生类的重写虚函数,至此实现动态多态

class base
{
public:
	virtual void func()
	{
		cout << "base work" << endl;;
	}
};

class son1 :public base
{
public:
	void func()
	{
		cout << "son1 work" << endl;
	}
};


class son2 :public base
{
public:
	void func()
	{
		cout << "son2 work" << endl;
	}
};

int main()
{
	
	son1 a;
	base* b = &a;
	b->func();//son1 work
	return 0;
}

程序中调用基类中的虚函数,由于多态最终是派生类1的重写的函数被调用

注意这只是多态的实现,正常未重写虚函数的基类一样可以调用虚函数

int main()
{
	base b ;
	b.func();//base work
	return 0;
}

多态的继承关系

由上面的原理分析,派生类必须能访问到基类的虚函数指针才能改变指向所以不能private继承基类

至于保护继承,笔者试过了也不行报错C2243,转化存在但无法访问,看了一下好像微软编译器禁止这么干了所以还是老老实实public继承把

基类中的虚函数应该放到public下,毕竟要公开访问权限才能让外部调用实现多态

至于派生类重新写的虚函数,放在任意位置都可以,虚函数指针可以直接访问虚函数列表,多态发生时基类的虚函数指针和重写的虚函数列表都是一个派生类下的,拥有互相访问权限

虚基类

当基类中的虚函数没有定义时即为纯虚函数,有纯虚函数的基类即为虚基类,虚基类无法实例化,但可以实例化指针!这点是多态的主要实现方法

纯虚函数在基类中无定义只有声明,同时在函数结尾加上=0;即可

class base
{
public:
	virtual void func() = 0;
};

一般用于基类仅作为被继承作用时,基类的函数没有实际具体意义

基类此时相当于一个抽象类,继承虚基类的派生类完成具体操作

多态中的构造函数

由于继承关系,派生类继承基类,所以会优先调用基类构造函数,在调用派生类构造函数

class base
{
public:
	base()
	{
		cout << "base create" << endl;
	}
	virtual void func() = 0;
};

class son1 :public base
{
public:
	son1()
	{
		cout << "son1 create" << endl;
	}
	void func()
	{
		cout << "son1 work" << endl;
	}
};


int main()
{
	son1 a;
	base* b=&a;
	b->func();
	return 0;
}

多态中的析构函数

由于继承关系,优先调用派生类中的析构函数

由于使用多态方法时,是对抽象基类操作,这是使用new delete操作符可能造成内存泄漏

class base
{
public:
	virtual void func() = 0;
	~base()
	{
		cout << "base delete" << endl;
	}
};

class son1 :public base
{
public:
	~son1()
	{
		cout << "son1 delete" << endl;
	}
	void func()
	{
		cout << "son1 work" << endl;
	}
};


int main()
{
	base* b = new son1;
	b->func();
	delete b;
	return 0;
}

如上虚基类指针接受new的派生类指针,最后释放基类指针(这是合理的,因为赋值操作,相当于基类的内存空间是new开辟的)最后的运行结果显示只调用了基类的析构函数,这是不合理的,如果派生类除了重写的虚函数还有有别的数据,那么最后就有被new出来的数据空间未被释放。

有一个解决方法显而易见,直接实例化派生类,最后delete就可以

还额外提供了另一种方法,使用虚析构函数

虚析构函数加上virtual即可,在基类中有定义加上virtual关键字的析构函数即为虚析构函数

同时可以直接将析构函数定义为纯虚函数,

纯虚析构函数与析构函数的区别:

1.有纯虚析构函数的基类即为虚基类,无法实例化

2.纯虚析构函数也要有定义实现同时必须定义在类外,必须有定义

因为如果你声明了析构函数,就不会调用系统默认的析构函数,所以你声明的析构函数必须有定义才能被调用

class base
{
public:
	virtual void func() = 0;
	virtual ~base() = 0;
};
base::~base()
{
	cout << "base delete" << endl;
}


class son1 :public base
{
public:
	~son1()
	{
		cout << "son1 delete" << endl;
	}
	void func()
	{
		cout << "son1 work" << endl;
	}
};


int main()
{
	base* b = new son1;
	b->func();
	delete b;
	return 0;
}

一个具体多态案例

本人对多态的理解是,抽象出一个公用接口基类,通过派生类重写虚函数实现具体方法

这是我在b站看到一个比较好的案例

电脑由显卡内存cpu等多部分组成,而每个部件都是抽象的,通过具体inter lenovo公司生产具体产品实现据具体功能,组装一台电脑需要具体的零件实现各种操作

#include<iostream>
using namespace std;


/*
*虚基类 实现
* 使用虚析构函数用于处理每个零件的数据
*/

class cpu
{
public:
	cpu()
	{
		cout << "virtual cpu create" << endl;
	}
	virtual ~cpu()
	{
		//可处理基类其余数据
		cout << "virtual cpu delete" << endl;
	}
	virtual void cpu_work() = 0;
private:
	int cpu_data;
};

class memory
{
public:
	memory()
	{
		cout << "virtual memory create" << endl;
	}
	virtual ~memory()
	{
		//可处理基类其余数据
		cout << "virtual memory delete" << endl;
	}
	virtual void memory_work() = 0;
private:
	int memory_data;
};

class card
{
public:
	card()
	{
		cout << "virtual card create" << endl;
	}
	virtual ~card()
	{
		//可处理基类其余数据
		cout << "virtual card delete" << endl;
	}
	virtual void card_work() = 0;
private:
	int card_data;
};



/*
*具体方法实现
* 重写抽象方法和虚构函数
* 
*/
class inter_cpu :public cpu
{
public:
	inter_cpu()
	{
		cout << "inter cpu create" << endl;
	}
	void cpu_work()
	{
		cout << "inter cpu work" << endl;
	}
	~inter_cpu()
	{
		cout << "inter cpu delete" << endl;
	}
private:
	string id;
};

class lenovo_memory :public memory
{
public:
	lenovo_memory()
	{
		cout << "lenovo memory create" << endl;
	}
	void memory_work()
	{
		cout << "lenovo memory work" << endl;
	}
	~lenovo_memory()
	{
		cout << "lenovo memory delete" << endl;
	}
private:
	string id;
};

class inter_card :public card
{
public:
	inter_card()
	{
		cout << "inter card create" << endl;
	}
	void card_work()
	{
		cout << "inter card work" << endl;
	}
	~inter_card()
	{
		cout << "inter card delete" << endl;
	}
private:
	string id;
};





class computer
{
public:
	computer(cpu* Cpu, card* Card, memory* Memory) : Cpu(Cpu), Card(Card), Memory(Memory) {};
	void computer_dowork()
	{
		Cpu->cpu_work();
		Card->card_work();
		Memory->memory_work();
	}
	~computer()
	{
		delete Cpu;
		delete Card;
		delete Memory;
	}
private:
	cpu* Cpu;
	card* Card;
	memory* Memory;

};





int main()
{
	cpu* CPU = new inter_cpu;
	card* CARD = new inter_card;
	memory* MEMORY = new lenovo_memory;
	computer com(CPU, CARD, MEMORY);
	com.computer_dowork();

	return 0;
}

非常好的案例,爱来自新乡

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值