先看代码:
1 #include<iostream>
2 using namespace std;
3
4 class Parent {
5 public:
6 Parent() :a(100), b(200), c(300)
7 {
8
9 p = new char[10];
10 //strcpy(p, "abc");
11 cout << "parent 无参构造。。。
";
12 }
13 Parent(int test) :a(1000), b(2000), c(3000)
14 {
15 p = new char[10];
16 //strcpy(p, "abc");
17 cout << "parent 有参构造。。。
";
18 }
19 ~Parent()
20 {
21 delete[] p;
22 cout << "Parent 析构。。。
";
23 }
24 int a;
25 int b;
26 int c;
27 char *p;
28 void p_print()
29 {
30 cout << "a b c is" << a << " " << b << " " << c << endl;
31 }
32
33 };
34 class Child1 : public Parent
35 {
36 public:
37 Child1() :Parent(1),a(10), b(0), c(0)
38 {
39 p = new char[10];
40 // strcpy(p, "abc");
41 cout << "child1 构造
";
42 }
43 ~Child1()
44 {
45 delete[] p;
46 cout << "child1 析构,,,
";
47 }
48 void c1_print()
49 {
50 cout << "a b c is" << a << " " << b << " " << c << endl;
51 }
52
53 int a;
54 int b;
55 int c;
56 char *p;
57 };
58 class Child2 : public Child1
59 {
60 public:
61 Child2() :Child1(), b(2), c(3)
62 {
63 p = new char[10];
64 //strcpy(p, "abc");
65 cout << "child2 构造
";
66 }
67 ~Child2()
68 {
69 delete[] p;
70 cout << "child2 析构,,,
";
71 }
72 void c2_print()
73 {
74 cout << "a b c is" << Parent::a << " " << b << " " << c << endl;
75 }
76 //int a;
77 int b;
78 int c;
79 char *p;
80 };
81 /*
82 class Child3 : public Child1, public Child2
83 {
84 public:
85 Child3() : Child1(), Child2(), b(20), c(30) { cout << "child 构造
"; }
86 ~Child3()
87 {
88 cout << "child 析构,,,
";
89 }
90 void c3_print()
91 {
92 cout << "a b c is" << a << " " << b << " " << c << endl;
93 }
94 //int a;
95 int b;
96 int c;
97 };
98 */
99 void play()
100 {
101 Child2* c2 = new Child2;
102 delete c2;
103 }
104 int main()
105 {
106 //Child2* c2 = new Child2;
107 play();
108 return 0;
109 }
这样是没问题的(c++编译器会以构造相反的顺序执行析构函数),但是,在很多时候,我们不能在调用函数末尾就delete掉这个内存,还需要和后续交互。比如作为函数参数,为了实现多态,我们函数参数是父类的指针,所以更常见和一般的设计思维是更改paly和main函数如下:
1 void play(Parent* p)
2 {
3 delete p;
4 }
5 int main()
6 {
7 Child2* c2 = new Child2;
8 play(c2);
9 return 0;
10 }
运行结果显示内存泄漏了,只析构了父类;所以我们有那么一种需求,要想和多态的效果一样,传什么指针去,自动析构应该析构的东西,更改代码,实现虚析构函数。
只用更改基类的析构函数,加上virtual关键字:
1 virtual ~Parent()
2 {
3 delete[] p;
4 cout << "Parent 析构。。。
";
5 }
这样就没有内存泄漏了。
知识简要:
如果基类的析构函数不是虚函数,则delete一个指向派生类对象的基类指针将产生未定义的行为。之前我们介绍过一个准则,如果一个类需要析构函数,那么它同样需要拷贝和赋值操作。基类的析构函数并不遵循上述准则,它是一个重要的例外。