博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从零开始学C++之虚函数与多态(二):纯虚函数、抽象类、虚析构函数
阅读量:6532 次
发布时间:2019-06-24

本文共 3415 字,大约阅读时间需要 11 分钟。

一、纯虚函数

虚函数是实现多态性的前提

需要在基类中定义共同的接口

接口要定义为虚函数

如果基类的接口没办法实现怎么办?

如形状类Shape

解决方法

将这些接口定义为纯虚函数

在基类中不能给出有意义的虚函数定义,这时可以把它声明成纯虚函数,把它的定义留给派生类来做

定义纯虚函数:
class 类名{
        virtual 返回值类型 函数名(参数表) = 0;
    };
纯虚函数不需要实现

二、抽象类

作用

抽象类为抽象和设计的目的而声明,将有关的数据和行为组织在一个继承层次结构中,保证派生类具有要求的行为。

对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。

注意

抽象类只能作为基类来使用。

不能声明抽象类的对象。

构造函数不能是虚函数,析构函数可以是虚函数

1、抽象类不能用于直接创建对象实例,可以声明抽象类的指针和引用

2、可使用指向抽象类的指针支持运行时多态性
3、派生类中必须实现基类中的纯虚函数,否则它仍将被看作一个抽象类

 

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
 
#include <iostream>
#include <vector>
#include <string>
using 
namespace std;
class Shape
{
public:
    
virtual 
void Draw() = 
0;
    
virtual ~Shape()
    {
        cout << 
"~Shape..." << endl;
    }
};
class Circle : 
public Shape
{
public:
    
void Draw()
    {
        cout << 
"Circle::Draw() ..." << endl;
    }
    ~Circle()
    {
        cout << 
"~Circle ..." << endl;
    }
};
class Square : 
public Shape
{
public:
    
void Draw()
    {
        cout << 
"Square::Draw() ..." << endl;
    }
    ~Square()
    {
        cout << 
"~Square ..." << endl;
    }
};
class Rectangle : 
public Shape
{
public:
    
void Draw()
    {
        cout << 
"Rectangle::Draw() ..." << endl;
    }
    ~Rectangle()
    {
        cout << 
"~Rectangle ..." << endl;
    }
};
void DrawAllShapes(
const vector<Shape *> &v)
{
    vector<Shape *>::const_iterator it;
    
for (it = v.begin(); it != v.end(); ++it)
    {
        (*it)->Draw();
    }
}
void DeleteAllShapes(
const vector<Shape *> &v)
{
    vector<Shape *>::const_iterator it;
    
for (it = v.begin(); it != v.end(); ++it)
    {
        
delete(*it);
    }
}
// 简单工厂模式
class ShapeFactory
{
public:
    
static Shape *CreateShape(
const string &name)
    {
        Shape *ps = 
0;
        
if (name == 
"Circle")
        {
            ps = 
new Circle;
        }
        
else 
if (name == 
"Square")
        {
            ps = 
new Square;
        }
        
else 
if (name == 
"Rectangle")
        {
            ps = 
new Rectangle;
        }
        
return ps;
    }
};
int main(
void)
{
    
//Shape s;      //Error,不能实例化抽象类
    vector<Shape *> v;
    
//Shape* ps;
    
//ps = new Circle;
    
//v.push_back(ps);
    
//ps = new Square;
    
//v.push_back(ps);
    
//ps = new Rectangle;
    
//v.push_back(ps);
    Shape *ps;
    ps = ShapeFactory::CreateShape(
"Circle");
    v.push_back(ps);
    ps = ShapeFactory::CreateShape(
"Square");
    v.push_back(ps);
    ps = ShapeFactory::CreateShape(
"Rectangle");
    v.push_back(ps);
    DrawAllShapes(v);
    DeleteAllShapes(v);
    
return 
0;
}

 

 

Shape类是抽象类,Draw函数是纯虚函数,Circle, Square, Rectangle都重新实现了Draw,在这里把Shape的析构函数声明为虚函数,那么delete 基类指针,会调用派生类的析构函数,这样不容易造成内存泄漏。虚函数可以让我们以一致的观点看待从同一基类继承下来的派生类对象,都是通过Shape* 去调用Draw,但能够实现不同的行为。

三、多态优点

多态性有助于更好地对程序进行抽象

控制模块能专注于一般性问题的处理

具体的操作交给具体的对象去做

多态性有助于提高程序的可扩展性

可以把控制模块与被操作的对象分开

可以添加已定义类的新对象,并能管理该对象

可以添加新类(已有类的派生类)的新对象,并能管理该对象

四、虚析构函数

析构函数可以声明为虚函数

delete 基类指针;

程序会根据基类指针指向的对象的类型确定要调用的析构函数

基类的析构函数为虚函数,所有派生类的析构函数都是虚函数

构造函数不得是虚函数

如果要操作具有继承关系的类的动态对象,最好使用虚析构函数。特别是在析构函数需要完成一些有意义的操作,比如释放内存

析构函数还可以是纯虚的。

 

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 
#include <iostream>
using 
namespace std;
// 对于一个没有任何接口的类,如果想要将它定义成抽象类,只能将虚析构函数声明为纯虚的
// 通常情况下在基类中纯虚函数不需要实现
// 例外是纯虚析构函数要给出实现。(给出一个空的实现即可)
class Base
{
public:
    
virtual ~Base() = 
0
    {
    }
};
class Derived : 
public Base
{
};
int main(
void)
{
    
//  Base b; //error, 抽象类
    Derived d;
    
return 
0;
}

 

 

// 对于一个没有任何接口的类,如果想要将它定义成抽象类,只能将虚析构函数声明为纯虚的

// 通常情况下在基类中纯虚函数不需要实现
// 例外是纯虚析构函数要给出实现。(给出一个空的实现即可)

 

参考:

C++ primer 第四版

Effective C++ 3rd
C++编程规范

你可能感兴趣的文章
让数据会思考会说话,为出海企业提供多样化数据智能解决方案
查看>>
我眼中的自动化测试框架设计要点
查看>>
FLIF:自由的无损图像格式
查看>>
Google开源Inception-ResNet-v2,提升图像分类水准
查看>>
Opera 出售细节曝光:昆仑出资1.68亿美元
查看>>
CentOS 5.3 下快速安装配置 PPTP ××× 服务器
查看>>
23种设计模式(15):备忘录模式
查看>>
java基础学习总结——IO流
查看>>
iOS获取APP ipa 包以及资源文件
查看>>
CentOS 7 关闭启动防火墙
查看>>
Vue-选项卡切换
查看>>
linux网络命令
查看>>
poj3984 迷宫问题(简单搜索+记录路径)
查看>>
Linux 服务器buff/cache清理
查看>>
算法试题 及其他知识点
查看>>
php课程---Json格式规范需要注意的小细节
查看>>
hadoop hdfs notes
查看>>
Java反射机制详解(3) -java的反射和代理实现IOC模式 模拟spring
查看>>
(2编写网络)自己动手,编写神经网络程序,解决Mnist问题,并网络化部署
查看>>
【转】如何使用分区助手完美迁移系统到SSD固态硬盘?
查看>>