[C++核心编程](九):类和对象——多态**

目录

多态的分类

多态的原理剖析

多态的优点

纯虚函数和抽象类

虚析构和纯虚析构


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

多态的分类

  • 静态多态

-函数重载运算符重载属于静态多态,复用函数名

-函数地址绑定-编译阶段确定函数地址

  • 动态多态

-派生类虚函数实现运行时多态

-函数地址绑定-运行阶段确定函数地址

动态多态满足条件:

1.继承关系

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

动态多态的使用:

父类的指针或者引用 指向 子类的对象

cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

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

class Cat:public Animal
{
public:
	void speak()
	{
		cout << "小猫在说话" << endl;
	}
};

class Dog :public Animal
{
public:
	void speak()
	{
		cout << "小狗在说话" << endl;
	}
};

//地址早绑定-编译阶段确定函数地址
//如果想执行让猫说话,就要进行晚绑定,使用虚函数
void dospeak(Animal& animal)
{
	animal.speak();
}

void test(void)
{
	Cat cat;
	dospeak(cat);

	Dog dog;
	dospeak(dog);
}

int main(void)
{
	test();
	return 0;
}

多态的原理剖析

父类内部结构vfptr-虚函数(表)指针 指向 vftable(表内记录虚函数的地址)

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

当父类的指针或者引用指向子类对象时,发生多态。

多态的优点

  • 代码组织结构清晰
  • 可读性强
  • 利于前期和后期的扩展和维护
  • (开闭原则)
cpp 复制代码
#include <iostream>
#include <string>

using namespace std;
//举例实现一个简易的计算器
class AbstractCalulator
{
public:
	//虚函数
	virtual int getResult()
	{
		return 0;
	}
	int n_number;
	int m_number;
};

class AddCalculator :public AbstractCalulator
{
public:
	int getResult()
	{
		return n_number + m_number;
	}
};

class SubCalculator :public AbstractCalulator
{
public:
	int getResult()
	{
		return n_number - m_number;
	}
};

class MulCalculator :public AbstractCalulator
{
public:
	int getResult()
	{
		return n_number * m_number;
	}
};

void test(void)
{
	AbstractCalulator* abc = new AddCalculator;
	abc->n_number = 10;
	abc->m_number = 10;

	cout << abc->n_number << "+" << abc->m_number << "=" << abc->getResult() << endl;
	delete abc;

	abc = new SubCalculator;
	abc->n_number = 10;
	abc->m_number = 10;

	cout << abc->n_number << "-" << abc->m_number << "=" << abc->getResult() << endl;
	delete abc;

	abc = new MulCalculator;
	abc->n_number = 10;
	abc->m_number = 10;

	cout << abc->n_number << "*" << abc->m_number << "=" << abc->getResult() << endl;
	delete abc;
}

int main(void)
{
	test();
	return 0;
}

纯虚函数和抽象类

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

纯虚函数语法:virtual 返回值类型 函数名 (参数列表) = 0;

当类中有了纯虚函数,这个类被称为 抽象类

抽象类特点:

  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

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

};

class Son:public Base
{
public:
	void func()
	{
		cout << "Son 's func()" << endl;
	}
};

void test(void)
{
	//Base base; 抽象类无法实例化对象
	//new Base;  抽象类无法实例化对象

	Base * base = new Son;
	base->func();
}

int main(void)
{
	test();
	return 0;
}

虚析构和纯虚析构

如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码导致内存泄漏 ,解决办法是将父类的析构函数改为虚析构 或者纯虚析构;

虚析构和纯虚析构的共性

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

虚析构和纯虚析构的区别

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

虚析构语法:virtual ~类名(){}

纯虚析构语法:virtual ~类名() = 0; 类名::~类名(){}

cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

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

AbstractAnimal::~AbstractAnimal()
{
	cout << "AbstractAnimal 析构函数调用" << endl;
}

class Dog :public AbstractAnimal
{
public:
	Dog(string name)
	{
		cout << "Dog 构造函数调用" << endl;
		m_name = new string(name);
	}
	~Dog()
	{
		if (m_name != NULL)
		{
			cout << "Dog 析构函数调用" << endl;
			delete m_name;
			m_name = NULL;
		}
	}
	void speak()
	{
		cout << *m_name << "  Dog speaking" << endl;
	}
	string* m_name;
};

void test(void)
{
	AbstractAnimal* abs = new Dog("Tom");
	abs->speak();
	delete abs;
}

int main(void)
{
	test();
	return 0;
}

推荐文章:C++核心编程(八):类和对象------继承**

相关推荐
郝学胜_神的一滴15 小时前
CMake 034:生成器表达式:解耦构建时序、精简分支逻辑的终极利器
c++·cmake
见过夏天1 天前
C++ 基础入门完全指南
c++
用户805533698033 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
BadBadBad__AK3 天前
线段树维护区间 k 次方和
c++·数学·算法·stl
卷无止境4 天前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
卷无止境4 天前
OpenMPI、MPICH 与 OpenMP:关系、核心概念与架构全解
c++·后端
郝学胜_神的一滴5 天前
CMake 30:循环语法全解|foreach_while双循环精讲、迭代技巧与实战避坑指南
c++·cmake
卷无止境7 天前
C++ 的Eigen 库全解析
c++
卷无止境7 天前
现代 C++特性大盘点:一门脱胎换骨的老语言
c++·后端
郝学胜_神的一滴7 天前
CMake 27:缓存变量的特性、语法、类型与实操全解
c++·cmake