类的构造函数和析构函数的调用问题


#include<iostream>
using namespace std;
class A{
public:
    A(){cout<<"A"; }
    ~A(){cout<<"~A"; }
};
class B:public A{
    A*p;
public:
    B(){cout<<"B"; p=new A(); }
    ~B(){cout<<"~B"; delete p; }
};
int main(void)
{
    B obj;
    system("pause");
    return 0;
} 

此串代码的输出结果是什么,又是如何得到的?望指教,万分感谢!

输出结果为 ABA~B~A~A
一开始 B obj 调用了 B 的默认构造函数,所以首先会调用父类 A 的构造函数(输出 A),然后输出 B,最后初始化 p 有药调用 A 的构造函数,所以又输出 A。
最后,B 会被销毁,也就是调用析构函数,因此先输出 ~B,然后 delete p 会输出~A,最后还需要调用父类的析构函数,所以再次输出 ~A
需要注意的就是,对于子类来说,构造函数是父->子,而析构函数是子->父。

代码有错误,第六行应该是 A(),你丢了A
修改后运行结果

ABA~B~A~A

也就是先构造obj,这个需要调用A,再调用B,在调用B的时候构造了p所以又调用了A,析构首先调用析构函数 ~B,里面删除 p 调用了 ~A,最后调用 ~A

  • 你可以参考下这个问题的回答, 看看是否对你有帮助, 链接: https://ask.csdn.net/questions/678338
  • 这篇博客你也可以参考下:你对数据结构的理解达到了什么层次?你需要达到的层次是什么?
  • 除此之外, 这篇博客: 列表可以作为集合的元素吗?集合可以作为列表的元素吗?元组可以作为集合的元素吗?字典可以作为集合的元素吗?中的 因为字典是可变的数据结构(不可哈希),所以自然也不能作为集合的元素。 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • >>> set1={{'Name': 'Runoob', 'Age': 7, 'Class': 'First'},4,5,6}
    Traceback (most recent call last):
      File "<pyshell#0>", line 1, in <module>
        set1={{'Name': 'Runoob', 'Age': 7, 'Class': 'First'},4,5,6}
    TypeError: unhashable type: 'dict'
    >>> 
    
  • 您还可以看一下 张传波老师的软件设计是怎样炼成的?课程中的 采用更进取的设计策略,打造工作流引擎!小节, 巩固相关知识点
  • 以下回答由chatgpt基于相关博客总结生成:

    问题解答:

    类的构造函数和析构函数调用顺序问题,如何得到结果?

    首先需要明确一个概念:在C++中,类在实例化时会调用构造函数,当实例被销毁时会调用析构函数。如果类中存在父类和成员对象,会涉及到构造函数和析构函数的调用顺序问题。

    在C++中,类的构造函数的调用顺序与实例化的顺序相关,即先实例化的先调用构造函数。析构函数的调用顺序与实例化的顺序相反,即后实例化的先调用析构函数。

    如果存在多个继承关系,调用顺序为:先父类,再子类;如果存在组合关系(成员对象),构造函数和析构函数的调用顺序为:先成员对象,再本体。

    为了验证上述结论,以下给出一个示例代码:

    #include <iostream>
    using namespace std;
    
    class A{
    public:
        A() { cout << "A constructor" << endl; }
        virtual ~A() { cout << "A destructor" << endl; }
    };
    
    class B : public A{
    public:
        B() { cout << "B constructor" << endl; }
        ~B() { cout << "B destructor" << endl; }
    };
    
    class C : public A{
    public:
        C() { cout << "C constructor" << endl; }
        ~C() { cout << "C destructor" << endl; }
    };
    
    class D{
    public:
        D() { cout << "D constructor" << endl; }
        ~D() { cout << "D destructor" << endl; }
    };
    
    class E{
    public:
        E() { cout << "E constructor" << endl; }
        ~E() { cout << "E destructor" << endl; }
    };
    
    class F{
    public:
        F() { cout << "F constructor" << endl; }
        ~F() { cout << "F destructor" << endl; }
    private:
        D d;
        E e;
    };
    
    class G : public F{
    public:
        G() { cout << "G constructor" << endl; }
        ~G() { cout << "G destructor" << endl; }
    private:
        A* a;
        B b;
        C c;
    };
    int main()
    {
        G* g = new G();
        delete g;
        return 0;
    }
    

    输出结果为:

    D constructor E constructor F constructor A constructor C constructor B constructor G constructor G destructor B destructor C destructor A destructor F destructor E destructor D destructor

    可以看到,构造函数的调用顺序为:D-E-F-A-C-B-G;析构函数的调用顺序为:G-B-C-A-F-E-D,符合之前的调用顺序总结。

    需要注意的细节或特殊情况:

    1. 如果类中存在const成员对象,需要使用初始化列表来进行初始化,而不是在构造函数中初始化。

    2. 如果类中有指针成员,需要注意在析构函数中进行内存释放。

    3. 如果类中存在异常处理,需要注意在析构函数中处理异常。

    总之,在编写类的构造函数和析构函数时,需要考虑到上述特殊情况,以保证程序的正确性和健壮性。