C++黑马程序员——P138,140. 纯虚函数和抽象类,虚析构和纯虚析构

  • P138. 类和对象——多态——纯虚函数和抽象类
  • P140. 类和对象——多态——虚析构和纯虚析构
  • P138. 纯虚函数和抽象类

 在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容

因此可以将虚函数改为 纯虚函数

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

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

抽象类特点:

● 无法实例化对象

● 子类必须重写抽象类(父类)中的纯虚函数,否则也属于抽象类

 1 //纯虚函数和抽象类
 2 class Base
 3 {
 4 public:
 5     //纯虚函数
 6     //只要有一个纯虚函数,这个类就是抽象类:无法实例化对象
 7     virtual void func() = 0;
 8 };
 9 
10 class Son :public Base
11 {
12 public:
13     //子类必须重写父类中的纯虚函数,否则也属于抽象类
14     virtual void func()
15     {
16         cout << "Son 的 func() 调用" << endl;
17     }
18 };
19 
20 void test01()
21 {
22     //Base b1;    //报错,抽象类无法实例化对象
23     //new Base;    //报错,抽象类无法实例化对象,堆区也不行
24     
25     //使用 多态 调用子类的函数
26     Base* base = new Son;
27     base->func();
28 }
29 
30 int main()
31 {
32     test01();
33     return 0;
34 }

运行结果:

  

 

  • P140. 虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

解决方式:将父类中的析构函数改为 虚析构 或者 纯虚析构

 

虚析构和纯虚析构共性:

可以解决父类指针释放子类对象

需要有具体的函数实现

 

虚析构和纯虚析构区别:

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

 1 class Animal
 2 {
 3 public:
 4     Animal()
 5     {
 6         cout << "Animal 构造函数调用" << endl;
 7     }
 8 
 9     ~Animal()
10     {
11         cout << "Animal 析构函数调用" << endl;
12     }
13 
14     //纯虚函数
15     virtual void speak() = 0;
16 };
17 
18 class Cat:public Animal
19 {
20 public:
21     Cat(string name)
22     {
23         cout << "Cat 构造函数调用" << endl;
24         m_Name = new string(name);
25     }
26 
27     ~Cat()
28     {
29         cout << "Cat 析构函数调用" << endl;
30         if (m_Name != NULL)
31         {
32             delete m_Name;
33             m_Name = NULL;
34         }
35     }
36 
37     virtual void speak()
38     {
39         cout << *m_Name << " Cat speak" << endl;
40     }
41     string* m_Name;
42 };
43 
44 void test01()
45 {
46     Animal* animal = new Cat("Tom");
47     animal->speak();
48     delete animal;
49 }
50 
51 int main()
52 {
53     test01();
54     return 0;
55 }

运行结果:

  

出现的问题:释放 animal 指针时,没有调用子类中的析构函数,子类中堆区的数据(m_Name)没有被释放,导致内存泄漏。(父类指针在析构时候,不会调用子类中析构函数)

 

虚析构语法:

virtual ~类名(){}

纯虚析构语法:

virtual ~类名()= 0;

类名 :: ~类名(){}(纯虚析构 在类外需要有具体的函数实现)

 

把上面 Animal 的析构函数改成虚析构:

1     //虚析构; 利用虚析构可以解决 父类指针释放子类对象时不干净的问题
2     virtual ~Animal()
3     {
4         cout << "Animal 析构函数调用" << endl;
5     }

运行结果:

  

 

把 Animal 的析构函数改成纯虚析构也可以:

 1 class Animal
 2 {
 3 public:
 4     Animal()
 5     {
 6         cout << "Animal 构造函数调用" << endl;
 7     }
 8 
 9     //纯虚析构,需要声明也需要实现 (在类外要有具体的实现,否则运行会报错)
10     //有了纯虚析构之后,这个类也属于抽象类,无法实例化对象
11     virtual ~Animal() = 0;
12 
13     //纯虚函数
14     virtual void speak() = 0;
15 };
16 
17 Animal::~Animal()
18 {
19     cout << "~Animal 的纯虚析构调用" << endl;
20 }

运行结果:

  

 

总结:

1. 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象

2. 如果子类中没有堆区数据,可以不写 虚析构或纯虚析构

3. 拥有纯虚析构函数的类也属于抽象类

(〃>_<;〃)(〃>_<;〃)(〃>_<;〃)

posted @ 2023-02-25 15:55  我会变强的  阅读(48)  评论(0编辑  收藏  举报