云计算百科
云计算领域专业知识百科平台

C++学习日记 Day10:多态

## 今天学了什么

今天学习C++的多态。

## 多态的分类

静态多态:函数重载、运算符重载、模板,复用函数名;

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

*注:静态多态和静态函数/静态变量(static)没有关系,只是相对于动态多态的叫法。

①静态多态和动态多态的区别:

  • 静态多态的函数地址早绑定:编译阶段 确定函数地址;
  • 动态多态的函数地址晚绑定:运行阶段 确定函数地址;

②动态多态的满足条件:

  • 有继承关系;
  • 子类要重写父类的虚函数;

③动态函数的使用:父类的指针或者引用要指向子类对象;

④多态的优点:

  • 代码组织结构清晰,可读性强;
  • 利于前期和后期的扩展和维护;

## 纯虚函数和抽象类

纯虚函数:在多态中,通常父类虚函数用处不大,主要都是子类调用重写,由此可以改为纯虚函数。

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

抽象类:当类中有纯虚函数,这个类也称为抽象类。

抽象类的特点:

  • ①无法实例化对象;
  • ②子类必须重写抽象类中的纯虚函数,否则也属于抽象类。

## 虚析构和纯虚析构

虚析构语法:

virtual ~ 类名(){}

纯虚析构语法:

class Base{

…

virtual ~ 类名(){}=0;

}

…

类名::~类名(){ …}

问题:在多态使用时,子类中有属性开辟到堆区,父类指针在释放时无法调用子类的析构函数,造成内存泄漏;

解决方法:将父类的析构改为虚析构/纯虚析构;

验证:

class Animal {
public:
Animal()
{
cout << "Animal 构造函数" << endl;

}
virtual void speak() = 0;

~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* an = new Cat("Tom");
an->speak();
delete an;
}

运行发现,打印结果没有cat的析构函数,在speak()处下断点,进入调试,发现在执行完speak函数以后,直接进入了Animal的析构函数,并没有进入Cat的析构函数,所以导致cat类的m_name指针无法释放。将父类的析构改为虚析构,重复调试步骤,发现这一次进入了cat的析构函数,验证成功。

虚析构和纯虚析构的共性:

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

区别:纯虚析构的类属于抽象类,无法实例化对象。

*注:如果子类中没有堆区数据可以不写虚析构/纯虚析构。

## 练习

1.利用多态实现计算器:

#include<iostream>
using namespace std;

class calculator {
public:
virtual int getResult()
{
return 0;
}

int num1;
int num2;
};

class Addcalculator:public calculator {
public:
int getResult()
{
return num1 + num2;
}
};

class Subcalculator :public calculator {
public:
int getResult()
{
return num1 – num2;
}
};

class Mulcalculator :public calculator {
public:
int getResult()
{
return num1 * num2;
}
};

void test()
{
calculator* cal = new Addcalculator;
cal->num1 = 10;
cal->num2 = 10;
cal->getResult();
cout << cal->num1 << " + " << cal->num2 << " = " << cal->getResult() << endl;
delete cal;

cal = new Subcalculator;
cal->num1 = 10;
cal->num2 = 10;
cout << cal->num1 << " – " << cal->num2 << " = " << cal->getResult() << endl;
delete cal;

cal = new Mulcalculator;
cal->num1 = 10;
cal->num2 = 10;
cout << cal->num1 << " * " << cal->num2 << " = " << cal->getResult() << endl;
delete cal;
}

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

## 补充

虚函数调用链:

Animal *p=new Dog();

p->speak();

顺序:

1.p是父类指针,指向Dog类的对象;

2.Dog对象内部有_vfptr(virtual function pointer,虚函数指针);

3._vfptr指向Dog类的虚函数表;

4.虚函数表中[0]是Dog::speak;

5.最终执行Dog::speak。

赞(0)
未经允许不得转载:网硕互联帮助中心 » C++学习日记 Day10:多态
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!