C++笔记 08(多态)

一. 多态

1. 多态的基本概念

多态是C++面向对象三大特性之一

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

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

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

class Cat::public Animal
{
	public:
		void speak()
		{
			cout<<"小猫在说话"<<endl;
		}
};  
/*3. 原因:地址早绑定,在编译阶段就确定了函数地址,不管传入什么动物,
都会走animal里面的speak函数*/

void Dospeak (Animal &animal)
{
	animal.speak();
}

void test01()
{
	Cat cat;   //1.想执行的是“小猫在说话”
	Dospeak(cat);
}

int main()
{
	test01(); //2. 但实际执行“动物在说话”
}

/*4. 如果想执行让猫说话,那么这个函数地址就不能提前绑定,
						需要在运行阶段进行绑定,地址晚绑定*/

修改如下:

class Animal
{
	public:
		 virtual void speak()  /*函数前面加上virtual关键字,
		 变成虚函数,那么编译器在编译的时候就不能确定函数调用了*/
		{
			cout<<"动物在说话"<<endl;
		}
};

class Cat::public Animal
{
	public:
		void speak() /*重写,函数返回值类型、函数名、参数、
						列表完全相同,virtual void speak();
					其中,virtual关键字在子类重写函数中可写可不写*/
		{
			cout<<"小猫在说话"<<endl;
		}
};  

void Dospeak (Animal &animal)
{
	animal.speak();
}

void test01()
{
	Cat cat;   
	Dospeak(cat);
}

int main()
{
	test01(); //输出的是“小猫在说话”
}

动态多态满足条件: 1)有继承关系; 2)子类重写父类虚函数;
动态多态使用条件:父类指针或引用指向子类对象 (Animal & animal =cat)

2. 多态的原理剖析

在这里插入图片描述
当子类重写父类的虚函数,子类中的虚函数表内部会替换成子类的虚函数地址;

到底什么是C++的多态?
当父类的指针或者引用指向子类对象时候,发生多态

Animal & animal =cat; /*由于指向的是cat对象,
							会从Cat类中的虚函数表中找这个函数*/
animal.speak();

3. 多态案例-计算机类

案例描述:分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算机类。

#include<iostream>
#include<string>
using namespace std;

class Calculator
{
	public:
		virtual int getresult() 
		{
			return 0;
		}
	public:
			int num1;
	   		int num2;
};
 
class Add :public Calculator 
{
	public:virtual int getresult() 
	{
		return num1 + num2;
	}
};

class Mul :public Calculator
{
	public:
		virtual int getresult() 
		{
			return num1 * num2;
		}
};

void test01() 
{
	/*Add add;
	add.num1 = 10;
	add.num2 = 20;
	cout << add.num1 << "+" << add.num2 << "=" 
								<< add.getresult() << endl;*/
	Calculator* abc = new Add;/*new Add相当于创建了一个子类的对象;
						此处是父类的指针指向了子类的对象*/
	abc->num1 = 10;
	abc->num2 = 20;
	cout << abc->num1 << "+" << abc->num2 << "="
	 << abc->getresult() << endl;
	delete abc;
}
int main() 
{
	test01();
	system("pause");
	return 0;
}

多态的优点:
1)代码组织结构清晰
2)可读性强
3)利于前期和后期的扩展以及维护
总结:C++开发提倡利用多态设计程序架构,因为多态优点很多

4. 纯虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改写为纯虚函数。

纯虚函数语法:virtual 返回值类型 函数名(参数列表)=0 ;
当类中有了纯虚函数,这个类也称为抽象类

抽象类特点:
1)无法实例化对象;
2)子类必须重写抽象类中的纯虚函数,否则也属于抽象类

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

class Son: public Base
{
	public:
		virtual void func()
		{
			cout<<"func调用"<<endl;
		}
};

void test01()
{
	//Base b ; 抽象类是无法实例化对象
	//new Base; 抽象类是无法实例化对象
	Son s; //子类必须重写父类中的纯虚函数,否则无法实例化对象
	Base *base=new Son; 
	base->func();
	/*其实多态的目的就是让函数的接口更加通用化,通过一个父类指针,
				由于你创建的对象不同,指向对象不同,可以调用多种确切的函数*/
}
int main()
{
	... ...
}

5.虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。

解决方式:将父类中的析构函数改为虚析构或者纯虚析构

虚析构和纯虚析构共性:
1)可以解决父类指针释放子类对象;
2)都需要有具体的函数实现

虚析构和纯虚析构区别:
如果是纯虚析构,该类属于抽象类,无法实例化对象

虚析构语法: virtual ~类名(){ }
纯虚析构语法:(需要声明也需要实现)virtual ~ 类名() = 0;类名::~ 类名() { }

class Animal
{
	public:
		virtual void speak()=0; //纯虚函数
		Animal()
		{
			cout<<"Animal构造函数调用"<<endl;
		}
		~Animal()
		{
			cout<<"Animal析构函数调用"<<endl;
		}
};

class Cat:public Animal
{
	public:
		Cat(string name)
		{
			cout<<"Cat构造函数调用"<<endl;
			m_Name=new string(name);
		}
		virtual void speak()
		{
			cout<<*m_Name<<"小猫在说话"<<endl;
		}
		~Cat ()
		{
			if(m_Name!=NULL)
			{
				cout<<"Cat析构函数调用"<<endl;
				delete m_Name;
				m_Name=NULL;
			}
		}
		string *m_Name;
};

void test()
{
	Animal *animal=new Cat("Tom");
	animal->speak();
	delete animal; 
	/*父类指针在析构时,不会调用子类中析构函数,导致子类如果有堆区属性,
				出现内存泄漏*/
}
int main() {... ...}

打印结果为:
Animal构造函数调用
Cat构造函数调用
Tom小猫在说话
Animal析构函数调用

没有“Cat析构函数调用”,说明堆区的数据没有释放干净,造成内存泄漏

解决办法,把父类的析构函数~Animal(){... ...}
改为virtual ~Animal() {... ...}即可

利用虚析构可以解决父类指针释放子类对象时不干净的问题
纯虚析构和虚析构只需要有一个

如果子类中没有堆区数据,可以不写为虚析构或者纯虚析构

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值