多态基本知识点

多态

导航:
1.多态基本概念
2.多态小案例
3.纯虚函数
4.虚析构与纯虚析构

———————————————————————————————————
多态的基本概念

多态分为两类
1.静态多态:函数重载以及运算符重载都属于静态多态,复用函数名
2.动态多态:派生类虚函数实现运行多态

如何区分:
静态多态的函数地址早绑定——编译阶段确定函数地址
动态多态的函数地址晚绑定——运行阶段确定函数地址

动态多态的满足条件:
1.具有继承关系。
2.子类重写父类的虚函数。(虚函数就是在成员函数前加上关键字virtual)
其中重写条件:类中函数的返回值类型,函数名,参数列表必须完全相同

动态多态的使用方法:父类的指针或者引用指向子类地址。

例子:

#include <iostream>
#include <string>
using namespace std;
class cost   //定义一个父类
{
public:
	void shop()	//仅仅只是一个成员函数,函数地址早绑定,也就是早确定函数地址
	{
		cout<<"最高消费0元"<<endl;
	}
};

class shoper1:public cost	//定义一个子类
{
public:
	void shop()
	{
		cout<<"最高消费1000元"<<endl;
	}
};

class shoper2:public cost
{
public:
	void shop()
	{
		cout<<"最高消费1500元"<<endl;
	}
};

void doshop(cost &cs)	//父类的指针或者引用可以指向子类对象
{
	cs .shop();  //进行成员函数调用
}

void test01()
{
	shoper1 s1;
	doshop(s1);   //进行引用传递

	shoper2 s2;
	doshop(s2);
}

int main()
{
	test01();
	system("pause");
	return 0;
}

运行程序:

最高消费0元
最高消费0

如何将其重写,然后输出该有结果
这时候就要在父类中的函数前加上virtual,此时便为虚函数,是动态多类,函数地址晚绑定,就可以就行重写。

例如:

public:
	virtual void shop()	//加上virtual则叫做虚函数,这样函数地址是晚绑定,不加则就是早绑定,确定了函数地址
	{
		cout<<"最高消费0元"<<endl;
	}
};

这个时候再运行程序:

最高消费1000元
最高消费1500

———————————————————————————————————
类中仅有一个函数,那么就是一个空类,占1个字节
若类中有一个虚函数,那么占4个字节
虚函数-vfptr (虚函数表指针)指向vftable-(虚函数表)

当子类重写父类的虚函数
子类的虚函数表内部会替换成子类虚函数地址

利用普通和多态写的案例:

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

//普通写法
class Calculator
{
public:

	int getresult(string oper)
	{
		if(oper == "+")
		{
			return num1+num2;
		}
		if(oper == "-")
		{
			return num1-num2;
		}
		if(oper == "*")
		{
			return num1*num2;
		}
		return 0;
	}

	int num1;
	int num2;
};

//普通的进行测试
void test()
{
	Calculator c1;
	c1.num1 = 100;
	c1.num2 = 100;
	cout<<c1.num1<<"+"<<c1.num2<<"="<<c1.getresult("+")<<endl;
	cout<<c1.num1<<"-"<<c1.num2<<"="<<c1.getresult("-")<<endl;
	cout<<c1.num1<<"*"<<c1.num2<<"="<<c1.getresult("*")<<endl;
}

//利用多态进行写
class AbstractCalculator
{
public:

	virtual int getresult()
	{
		return 0;
	}

	int num1;
	int num2;
};

//加法
class AddCalculator:public AbstractCalculator
{
public:
	int getresult()
	{
		return num1 + num2;
	}
};

//减法
class SubCalculator:public AbstractCalculator
{
public:
	int getresult()
	{
		return num1 - num2;
	}
};

//乘法
class MulCalculator:public AbstractCalculator
{
public:
	int getresult()
	{
		return num1 * num2;
	}
};

void test02()
{
	//调用加法
	AbstractCalculator *abs = new AddCalculator;//父类指针或者引用指向子类对象
	abs->num1 = 100;
	abs->num2 = 100;
	cout<<abs->num1<<"+"<<abs->num2<<"="<<abs->getresult()<<endl;
	delete abs;	//手动进行释放
	
	//调用减法
	abs = new SubCalculator;
	abs->num1 = 100;
	abs->num2 = 100;
	cout<<abs->num1<<"-"<<abs->num2<<"="<<abs->getresult()<<endl;
	delete abs;

	//调用乘法
	abs = new MulCalculator;
	abs->num1 = 100;
	abs->num2 = 100;
	cout<<abs->num1<<"-"<<abs->num2<<"="<<abs->getresult()<<endl;
	delete abs;
}

int main()
{
	test02();
	system("pause");
	return 0;
}

用多态写的好处是:有利于扩展,结构清晰,可读性强

———————————————————————————————————
纯虚函数
语法:virtual 返回值类型 函数名 (参数列表) = 0
父类中含有一个纯虚函数,那么称为抽象类无法进行实例化
同时子类中也无法实例化对象,也被称为抽象类

例子:

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

class Base
{
public:
	//纯虚函数
	virtual void func() = 0;  
};

class son:public Base
{
public:
	virtual void func()
	{
		cout<<"子类中func的调用"<<endl;
	}
};

void test()
{
	//Base b1;  //其中有纯虚函数,无法实例化
	//new Base; //也无法实例化
	Base *b = new son;
	b->func();
	delete b; //手动释放
}

int main()
{
	test();
	system("pause");
	return 0;
}

———————————————————————————————————虚析构与纯虚析构
共同点:

1.可以解决父类指针释放子类对象
2.都需要有具体的函数实现

区别:

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

虚析构:virtual ~类名() {}
纯虚析构: virtual ~类名() = 0 (需要声明,也要有代码的实现)

能用到纯析构,是因为要在堆区开辟新数据,父类指针释放子类对象
例子:

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

//纯析构和纯虚函数

class Animal
{
public:
	Animal()
	{
		cout<<"Aniaml构造函数调用"<<endl;
	}
	//纯虚函数
	virtual void speak() = 0;
	//利用虚析构可以解决 父类指针释放子类对象时不干净问题

	//纯虚析构 必须要有代码实现 需要声明也需要实现
	//有纯虚析构 也无法进行实例化对象
	virtual ~Animal() = 0;

	//虚析构
	/*virtual ~Animal()
	{
		cout<<"Aniaml析构函数调用"<<endl;
	}*/
};

//类外进行代码实现
Animal::~Animal()
{
	cout<<"Aniaml纯析构函数调用"<<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 test01()
{
	Animal *animal = new Cat("Tom"); //父类指针在析构时不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏存在
	animal->speak();
	delete animal;
}

int main()
{
	test01();
	system("pause");
	return 0;
}

总结:

1.虚析构或纯虚析构就是用来解决通过父类指针释放类对象
2.如果子类中没有堆区数据,可以不写为虚析构式或纯虚析构
3.拥有纯虚析构函数的类也属于抽象类,无法进行实例化

发布了8 篇原创文章 · 获赞 2 · 访问量 157

猜你喜欢

转载自blog.csdn.net/cl939974883/article/details/103986353