目录
多态的分类
多态的原理剖析
多态的优点
纯虚函数和抽象类
虚析构和纯虚析构
多态是C++面向对象三大特性之一
多态的分类
- 静态多态
-函数重载和运算符重载属于静态多态,复用函数名
-函数地址早绑定-编译阶段确定函数地址
- 动态多态
-派生类和虚函数实现运行时多态
-函数地址晚绑定-运行阶段确定函数地址
动态多态满足条件:
1.继承关系
2.子类要重写(函数返回值类型、函数名、参数列表完全相同)父类的虚函数
动态多态的使用:
父类的指针或者引用 指向 子类的对象
#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(表内记录虚函数的地址)
当子类重写父类的虚函数:子类中的虚函数表内部会替换成子类的虚函数地址
当父类的指针或者引用指向子类对象时,发生多态。
多态的优点
- 代码组织结构清晰
- 可读性强
- 利于前期和后期的扩展和维护
- (开闭原则)
#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;
当类中有了纯虚函数,这个类被称为抽象类
抽象类特点:
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
#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; 类名::~类名(){}
#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++核心编程](八):类和对象——继承**