目录
4. 纯虚函数和抽象类
在多态中,通常父类中的虚函数都用于子类的重写,自身没什么意义,所以,可以改为纯虚
函数。
4.1. 纯虚函数语法
virtual 返回类型 函数名(参数列表)= 0;(不需要加{}哦)
当类中有至少一个纯虚函数时,该类又称抽象类。
4.2. 抽象类的特点
• 无法实例化对象。
• 其子类必须对其中的纯虚函数进行重写,否则子类也属于抽象类。
4.3. 代码演示
接下来,看看示例代码
class Base
{
public:
//纯虚函数
virtual void func() = 0;
};
class Son:public Base
{
public:
void func()
{
cout << "耶耶耶耶耶" << endl;
}
};
void text01()
{
//Base b;//此代码会报错
Base* a = new Son();
a->func();
}
以上代码应当不难理解,就是关于纯虚函数和抽象类在代码里的体现。(其中Base为抽象类)
5. 虚析构与纯虚析构
当子类中有堆区数据时,父类指针在释放时(Base* a = new Son();delete a;)无法调用到子类的析构函数,此时,就需要将父类的析构函数改成虚析构或纯虚析构。
5.1. 虚析构和纯虚析构的共性与区别
共性:
• 都可以解决父类指针释放子类对象的难题
• 都需要具体的函数
区别:
• 若有纯虚析构,则该类属于抽象类,无法实例化对象
5.2. 语法
虚析构——virtual ~类名(){}
纯虚析构—virtual ~类名(){} = 0;(要写;的)
5.3. 代码演示
接下来,上代码,先看看无虚析构时会怎样
//父类
class Animal
{
public:
//构造函数
Animal()
{
//指示Animal构造函数的调用
cout << "Animal构造函数调用" << endl;
}
//纯虚函数
virtual void speak() = 0;
//析构函数
~Animal()
{
//指示Animal析构函数的调用
cout << "Animal析构函数调用" << endl;
}
};
class Cat :public Animal
{
public:
//有参构造
Cat(string name)
{
//指示Cat构造函数的调用
cout << "Cat构造函数调用" << endl;
//在堆区开辟
m_Name = new string(name);
}
//重写
void speak()
{
cout << *m_Name << "喵喵叫" << endl;
}
//析构函数
~Cat()
{
//指示Cat析构函数的调用
cout << "Cat析构函数调用" << endl;
//对堆区数据的释放
if (m_Name != NULL)
{
delete m_Name;
m_Name = NULL;
}
}
//接收堆区数据的属性
string* m_Name;
};
void text01()
{
Animal* a = new Cat("小花");
a->speak();
delete a;
}
以上代码运行之后,应该是这样的
可以清晰地看到,子类Cat的析构函数没有被调用,如此,会出现内存的泄露。
只需在Animal的析构函数前面加virtual即可解决这个问题,在此不再列举。
将Animal的析构函数换成如下代码,即为纯虚析构
virtual ~Animal() = 0;
但是,在该代码中,如果如此写,就会报错,因为,上面的运行图中,Animal的析构函数是有调用的,而且,倘若,父类中也有属性开辟在堆区,那析构函数是不得不用的。
因此,代码要变成这样
class Animal
{
public:
Animal()
{
cout << "Animal构造函数调用" << endl;
}
virtual void speak() = 0;
virtual ~Animal() = 0;
/*{
cout << "Animal析构函数调用" << endl;
}*/
};
//类外
Animal:: ~Animal()
{
cout << "Animal纯析构函数调用" << endl;
}
另外,此时,Animal类为抽象类。
最后,问大家一下,虚析构和纯虚析构是用来干什么的来着?