多态下,构造函数和析构函数的顺序,以及父类、子类的转换

 

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cstring>
  5 using namespace std;
  6 
  7 #define ll long long
  8 
  9 const int maxn=1e5+10;
 10 
 11 class A {
 12 public:
 13     A() {
 14         cout << "build A" << endl;
 15     }
 16     virtual ~A() {
 17         cout << "delete A" << endl;
 18     }
 19     virtual void work() {
 20         cout << "work A" << endl;
 21     }
 22 };
 23 
 24 class B: public A {
 25 public:
 26     B() {
 27         cout << "build B" << endl;
 28     }
 29     ~B() {
 30         cout << "delete B" << endl;
 31     }
 32     virtual void work() override {
 33         cout << "work B" << endl;
 34     }
 35 };
 36 
 37 class C {
 38 public:
 39     C() {
 40         cout << "build C" << endl;
 41     }
 42     virtual ~C() {
 43         cout << "delete C" << endl;
 44     }
 45     virtual void work() {
 46         cout << "work C" << endl;
 47     }
 48 };
 49 
 50 class D: public C {
 51 public:
 52     D() {
 53         cout << "build D" << endl;
 54     }
 55     ~D() {
 56         cout << "delete D" << endl;
 57     }
 58     virtual void work() override {
 59         cout << "work D" << endl;
 60     }
 61     void init() {
 62         memset(a, 0, sizeof(a));
 63     }
 64 private:
 65     int a[1000];
 66 };
 67 
 68 int main()
 69 {
 70     cout << "test1:" << endl;
 71     A * obj1 = new B();
 72     obj1 -> work();
 73     delete obj1;
 74     obj1 = nullptr;
 75     cout << endl;
 76 /*
 77 build A
 78 build B
 79 work B
 80 delete B
 81 delete A
 82 */
 83 
 84 /*
 85 如果class A析构函数不写成虚函数
 86 build A
 87 build B
 88 work B
 89 delete A
 90 */
 91 
 92     cout << "test2:" << endl;
 93     A * obj2 = (A*)new B();
 94     obj2 -> work();
 95     delete obj2;
 96     obj2 = nullptr;
 97     cout << endl;
 98 //和上面一样的结果
 99 
100     //wrong 编译的时候
101     /*
102     B * obj3 = new A();
103     obj3 -> work();
104     delete obj3;
105     obj3 = nullptr;
106     */
107 
108     cout << "test3:" << endl;
109     B * obj4 = (B*)new A();
110     obj4 -> work();
111     delete obj4;
112     obj4 = nullptr;
113     cout << endl;
114 /*
115 build A
116 work A
117 delete A
118 */
119 
120     cout << "test4:" << endl;
121     C * obj5 = new D();
122     obj5 -> work();
123     delete obj5;
124     obj5 = nullptr;
125     cout << endl;
126 /*
127 build C
128 build D
129 work D
130 delete D
131 delete C
132 */
133 
134     cout << "test5:" << endl;
135     D * obj6 = (D*)new C();
136     obj6 -> work();
137     //obj6 -> init(); //运行时报错
138     delete obj6;
139     obj6 = nullptr;
140     cout << endl;
141 /*
142 build C
143 work C
144 delete C
145 */
146 
147     return 0;
148 }
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cstring>
  5 using namespace std;
  6 
  7 #define ll long long
  8 
  9 const int maxn=1e5+10;
 10 
 11 class A {
 12 public:
 13     A() {
 14         cout << "build A" << endl;
 15     }
 16     virtual ~A() {
 17         cout << "delete A" << endl;
 18     }
 19     virtual void work() {
 20         cout << "work A" << endl;
 21     }
 22 };
 23 
 24 class B: public A {
 25 public:
 26     B() {
 27         cout << "build B" << endl;
 28     }
 29     ~B() {
 30         cout << "delete B" << endl;
 31     }
 32     virtual void work() override {
 33         cout << "work B" << endl;
 34     }
 35 };
 36 
 37 class C {
 38 public:
 39     C() {
 40         cout << "build C" << endl;
 41     }
 42     virtual ~C() {
 43         cout << "delete C" << endl;
 44     }
 45     virtual void work() {
 46         cout << "work C" << endl;
 47     }
 48 };
 49 
 50 class D: public C {
 51 public:
 52     D() {
 53         cout << "build D" << endl;
 54     }
 55     ~D() {
 56         cout << "delete D" << endl;
 57     }
 58     virtual void work() override {
 59         cout << "work D" << endl;
 60     }
 61     void init() {
 62         memset(a, 0, sizeof(a));
 63     }
 64 private:
 65     int a[1000];
 66 };
 67 
 68 int main()
 69 {
 70     cout << "test1:" << endl;
 71     A * obj1 = new B();
 72     obj1 -> work();
 73     delete obj1;
 74     obj1 = nullptr;
 75     cout << endl;
 76 /*
 77 build A
 78 build B
 79 work B
 80 delete B
 81 delete A
 82 */
 83 
 84 /*
 85 如果class A析构函数不写成虚函数
 86 build A
 87 build B
 88 work B
 89 delete A
 90 */
 91 
 92     cout << "test2:" << endl;
 93     A * obj2 = (A*)new B();
 94     obj2 -> work();
 95     delete obj2;
 96     obj2 = nullptr;
 97     cout << endl;
 98 //和上面一样的结果
 99 
100     //wrong 编译的时候
101     /*
102     B * obj3 = new A();
103     obj3 -> work();
104     delete obj3;
105     obj3 = nullptr;
106     */
107 
108     cout << "test3:" << endl;
109     B * obj4 = (B*)new A();
110     obj4 -> work();
111     delete obj4;
112     obj4 = nullptr;
113     cout << endl;
114 /*
115 build A
116 work A
117 delete A
118 */
119 
120     cout << "test4:" << endl;
121     C * obj5 = new D();
122     obj5 -> work();
123     delete obj5;
124     obj5 = nullptr;
125     cout << endl;
126 /*
127 build C
128 build D
129 work D
130 delete D
131 delete C
132 */
133 
134     cout << "test5:" << endl;
135     D * obj6 = (D*)new C();
136     obj6 -> work();
137     //obj6 -> init(); //运行时报错
138     delete obj6;
139     obj6 = nullptr;
140     cout << endl;
141 /*
142 build C
143 work C
144 delete C
145 */
146 
147     return 0;
148 }

 

这段代码主要展示了 C++ 中类的继承、多态、虚函数以及对象的创建与销毁过程,我们来逐一分析各个测试用例的输出结果:

test1 分析

cpp
 
运行
 
 
 
 
A * obj1 = new B();
obj1 -> work();
delete obj1;
 
  • 当创建B对象时,会先调用父类A的构造函数,再调用B自身的构造函数,因此输出build Abuild B
  • work()是虚函数,通过基类指针调用时会执行实际指向对象(B)的work()方法,因此输出work B
  • A的析构函数是虚函数,删除基类指针时会先调用派生类B的析构函数,再调用基类A的析构函数,因此输出delete Bdelete A
如果A的析构函数不是虚函数,删除基类指针时只会调用基类A的析构函数,导致B的析构函数不会被执行,这就是为什么需要将基类析构函数声明为虚函数的原因。

test2 分析

cpp
 
运行
 
 
 
 
A * obj2 = (A*)new B();
 
这里的强制类型转换是多余的,因为BA的派生类,B*可以隐式转换为A*。所以这段代码的行为和 test1 完全相同,输出结果也一样。

test3 分析

cpp
 
运行
 
 
 
 
B * obj4 = (B*)new A();
 
  • 这里创建的是A对象,所以只调用A的构造函数:build A
  • 虽然通过强制类型转换将A*转为B*,但实际对象还是A,所以work()调用的是A的版本:work A
  • 删除时,由于实际对象是A,所以只调用A的析构函数:delete A
这种转换是不安全的,因为A对象不包含B的成员,可能会导致未定义行为。

test4 分析

cpp
 
运行
 
 
 
 
C * obj5 = new D();
 
  • 类似 test1,创建D对象时先调用C的构造函数,再调用D的构造函数:build Cbuild D
  • work()是虚函数,调用的是D的版本:work D
  • C的析构函数是虚函数,删除时先调用D的析构函数,再调用C的析构函数:delete Ddelete C

test5 分析

cpp
 
运行
 
 
 
 
D * obj6 = (D*)new C();
 
  • 这里创建的是C对象,所以只调用C的构造函数:build C
  • 实际对象是C,所以work()调用的是C的版本:work C
  • 删除时,实际对象是C,所以只调用C的析构函数:delete C
同样,这种将基类指针强制转换为派生类指针的做法是不安全的。

总结关键知识点:

  1. 派生类对象创建时,先调用基类构造函数,再调用派生类构造函数
  2. 虚函数实现多态:基类指针指向派生类对象时,调用的是派生类的虚函数实现
  3. 基类析构函数应声明为虚函数,以确保删除基类指针时能正确调用派生类析构函数
  4. 向下转型(基类指针转派生类指针)是不安全的,可能导致未定义行为
这些测试用例很好地展示了 C++ 面向对象编程中的多态性和对象生命周期管理的重要概念。
posted @ 2025-09-27 11:59  congmingyige  阅读(12)  评论(0)    收藏  举报