类和对象-多态-C++

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

1.分类

静态多态:函数重载和运算符重载属于静态多态,服用函数名。

动态多态:派生类和虚函数实现运行时多态。

2.区别

静态多态的函数地址早绑定--编译阶段确定函数地址。

动态多态的函数地址晚绑定--运行阶段确定函数地址。

3.出现的问题

我们想要的效果是输出“猫说话”,实际输出“动物在说话”,因为C++中允许父子类之间的类型转换,而且地址早绑定,在编译阶段就已经确定。

#include<iostream>
using namespace std;
#include<string>
class animal
{
public:
	void speak()
	{
		cout << "动物在说话" << endl;
	}
};
class cat:public animal
{
public:
	void speak()
	{
		cout << "猫在说话" << endl;
	}
};
void fun(animal &a)
{
	a.speak();
}
int main()
{
	cat a;
	fun(a);
	return 0;
}

4.解决方法

使用动态多态

1.原因:因为动态多态是地址晚绑定,只有在运行阶段才会绑定。

2.方法:将animal中的speak函数变为虚函数

3.代码实现

#include<iostream>
using namespace std;
#include<string>
class animal
{
public:
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
};
class cat:public animal
{
public:
	void speak()
	{
		cout << "猫在说话" << endl;
	}
};
void fun(animal &a)
{
	a.speak();
}
int main()
{
	cat a;
	fun(a);
	return 0;
}

这个是用引用实现的动态多态

5.动态多态满足条件

1.有继承关系

2.子类重写父类的虚函数(重写定义:函数返回值类型,函数名,参数列表要完全相同。)

6.动态多态使用场景

父类的指针引用执行子类对象

7.底层原理

就是指针介入,使后续可以更改了

8.案例一

1.题目

利用多态实现计算器

2.多态优点

如果想扩展新的内容,不需要修改源码,只需要添加新的代码就行,符合开闭原则,利于维护。

(开闭原则:对扩展进行开放,对修改进行关闭)

3.实现步骤

利用指针方法实现多态,上面的方法是利用引用实现多态。

4.代码实现

#include<iostream>
using namespace std;
#include<string>
class calculator
{
public:
	virtual int fun()
	{
		return 0;
	}
	int num1 = 10;
	int num2 = 10;
};
class jia:public calculator
{
public:
	int fun()
	{
		return num1 + num2;
	}
};
class jian :public calculator
{
public:
	int fun()
	{
		return num1 - num2;
	}
};
class cheng :public calculator
{
public:
	int fun()
	{
		return num1 * num2;
	}
};
void fun2()
{
	calculator* abc = new jia;
	abc->num1 = 10;
	abc->num2 = 10;
	cout << abc->num1 << "+" << abc->num2 << "=" << abc->fun() << endl;

	abc = new jian;
	abc->num1 = 10;
	abc->num2 = 10;
	cout << abc->num1 << "-" << abc->num2 << "=" << abc->fun() << endl;

	abc = new cheng;
	abc->num1 = 10;
	abc->num2 = 10;
	cout << abc->num1 << "*" << abc->num2 << "=" << abc->fun() << endl;
}
int main()
{
	calculator a;
	jia b;
	jian c;
	cheng d;
	fun2();
	return 0;
}

9.纯虚函数和抽象类

1.定义

在多态中,通常父类中的虚函数是毫无意义的,主要都是来调用子类重写的内容,因此可以将虚函数改为纯虚函数。(当类中有了抽象函数,这个类也叫做抽象类)

2.纯虚函数语法

virtual 返回值类型 函数名(参数列表)=0;

3.抽象类特点

1.无法实例化对象

2.子类必须重写抽象类。中的纯虚函数,否则也属于抽象类。

4.代码演示

#include<iostream>
using namespace std;
#include<string>
class calculator
{
public:
	virtual void fun() = 0;
};
class jia:public calculator
{
public:
	void fun()
	{
		cout << "子类调用" << endl;
	}
};
int main()
{
	jia b;
	calculator* abc = new jia;
	abc->fun();
	return 0;
}

5.案例二

1.题目

使用纯虚函数构造饮品

2.代码实现

#include<iostream>
using namespace std;
#include<string>
class abstractdrinking
{
public:
	//煮水
	virtual void boil() = 0;
	//冲泡
	virtual void brew() = 0;
	//倒入杯中
	virtual void pourincup() = 0;
	//加入辅料
	virtual void putsth() = 0;
	//整体实现
	void makedrink()
	{
		boil();
		brew();
		pourincup();
		putsth();
	}
};
class tea:public abstractdrinking
{
public:
	//煮水
	virtual void boil()
	{
		cout << "煮农夫山泉" << endl;
	}
	//冲泡
	virtual void brew() 
	{
		cout << "放入茶叶" << endl;
	}
	//倒入杯中
	virtual void pourincup()
	{	
		cout << "入杯中" << endl;
	}
	//加入辅料
	virtual void putsth()
	{
		cout << "加蜂蜜" << endl;
	}
};
void gowork(abstractdrinking *abc)
{
	abc->makedrink();
	delete abc;//释放
}
void maketea()
{
	gowork(new tea);
}
int main()
{
	maketea();
	return 0;
}

10.虚析构和纯虚析构

1.作用

多态使用时,如果有子类开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码,将父类中的析构函数改为虚析构和纯虚析构。

2.无虚析构和纯析构版

#include<iostream>
using namespace std;
#include<string>
class animal
{
public:
	animal()
	{
		cout << "animal构造函数调用" << endl;
	}
	~animal()
	{
		cout << "animal析构函数调用" << endl;
	}
	virtual void speak() = 0;
};
class cat:public animal
{
public:
	cat(string name)
	{
		cout << "cat构造函数调用" << endl;
		Name = new string(name);
	}
	~cat()
	{
		if (Name != NULL)
		{
			cout << "cat析构函数调用" << endl;
			delete Name;
			Name = NULL;
		}
		
	}
	string* Name;
	void speak()
	{
		cout << *Name<<"cat在说话" << endl;
	}
};
void fun()
{
	animal* abc = new cat("tom");
	abc->speak();
	delete abc;
}
int main()
{
	fun();
	return 0;
}

问题:cat的析构函数未被调用,所以Name的堆区未被释放,导致内存泄漏。

3.虚析构版代码

#include<iostream>
using namespace std;
#include<string>
class animal
{
public:
	animal()
	{
		cout << "animal构造函数调用" << endl;
	}
	virtual ~animal()
	{
		cout << "animal析构函数调用" << endl;
	}
	virtual void speak() = 0;
};
class cat:public animal
{
public:
	cat(string name)
	{
		cout << "cat构造函数调用" << endl;
		Name = new string(name);
	}
	~cat()
	{
		if (Name != NULL)
		{
			cout << "cat析构函数调用" << endl;
			delete Name;
			Name = NULL;
		}
		
	}
	string* Name;
	void speak()
	{
		cout << *Name<<"cat在说话" << endl;
	}
};
void fun()
{
	animal* abc = new cat("tom");
	abc->speak();
	delete abc;
}
int main()
{
	fun();
	return 0;
}

4.纯虚析构版代码

#include<iostream>
using namespace std;
#include<string>
class animal
{
public:
	animal()
	{
		cout << "animal构造函数调用" << endl;
	}
	virtual ~animal() = 0;
	virtual void speak() = 0;
};
animal::~animal()
{
	cout << "animal析构函数调用" << endl;
}
class cat:public animal
{
public:
	cat(string name)
	{
		cout << "cat构造函数调用" << endl;
		Name = new string(name);
	}
	~cat()
	{
		if (Name != NULL)
		{
			cout << "cat析构函数调用" << endl;
			delete Name;
			Name = NULL;
		}
		
	}
	string* Name;
	void speak()
	{
		cout << *Name<<"cat在说话" << endl;
	}
};
void fun()
{
	animal* abc = new cat("tom");
	abc->speak();
	delete abc;
}
int main()
{
	fun();
	return 0;
}

注意:纯虚析构要有声明和实现两部分组成。

4.区别

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

5.注意

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值