从零开始学C++之虚函数与多态(二):纯虚函数、抽象类、虚析构函数

一、纯虚函数

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

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

接口要定义为虚函数

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

如形状类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() = 
;

    
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 = 
;

        
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 
;

}

从零开始学C++之虚函数与多态(二):纯虚函数、抽象类、虚析构函数

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() =

{

}

};

class Derived : 
public Base

{

};

int main(
void)

{

    
//  Base b; //error, 抽象类
    Derived d;

    
return 
;

}

// 对于一个没有任何接口的类,如果想要将它定义成抽象类,只能将虚析构函数声明为纯虚的
// 通常情况下在基类中纯虚函数不需要实现
// 例外是纯虚析构函数要给出实现。(给出一个空的实现即可)

参考:

C++ primer 第四版
Effective C++ 3rd
C++编程规范

上一篇:eclipse集成lombok


下一篇:leecode 978. Longest Turbulent Subarray(最长连续波动序列,DP or 滚动数组)