栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

【C++】类构造函数、析构函数的调用顺序「完整版」

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【C++】类构造函数、析构函数的调用顺序「完整版」

一、全局变量、静态变量和局部变量

全局变量在程序开始时调用构造函数、在程序结束时调用析构函数。
静态变量在所在函数第一次被调用时调用构造函数、在程序结束时调用析构函数,只调用一次。
局部变量在所在的代码段被执行时调用构造函数,在离开其所在作用域(大括号括起来的区域)时调用析构函数。可以调用任意多次。


下面我们通过代码进行更详细的讲解:

#include 

using namespace std;

// 下面定义三个类,在调用构造、析构函数时进行输出
class A
{
public:
    A()
    {
        cout << "+ Constructor of An"; // 构造函数
    }
    ~A()
    {
        cout << "- Destructor of An"; // 析构函数
    }
};

class B
{
public:
    B()
    {
        cout << "+ Constructor of Bn";
    }
    ~B()
    {
        cout << "- Destructor of Bn";
    }
};

class C
{
public:
    C()
    {
        cout << "+ Constructor of Cn";
    }
    ~C()
    {
        cout << "- Destructor of Cn";
    }
};

// 下面进行测试

A global; // 全局变量

void function() // 被调用3次的函数
{
    cout << "Beginning of function.n"; // 函数开始
    static B static_object; // 静态变量
    { // 局部变量所在作用域开始
        C local_object; // 局部变量
    } // 局部变量所在作用域结束
    cout << "End of function.n"; // 函数结束
}

int main()
{
    cout << "Call function for 3 times ---------------n";
    // 调用函数3次
    for(int i = 1; i < 4; ++i) function();
    cout << "Main function exiting -------------------n";
    // 主函数结束
    return 0;
}
// 程序结束

执行结果如下:

+ Constructor of A
Call function for 4 times ---------------
Beginning of function.
+ Constructor of B
+ Constructor of C
- Destructor of C
End of function.
Beginning of function.
+ Constructor of C
- Destructor of C
End of function.
Beginning of function.
+ Constructor of C
- Destructor of C
End of function.
Main function exiting -------------------
- Destructor of B
- Destructor of A

首先,对于全局变量A global,它在程序开始时被调用,在程序结束时被析构。
其次,对于静态变量static B static_object,它在function第一次被调用时被构造,在程序结束时被析构。
然后,对于局部变量C local_object,每次函数执行到它那一步时都被调用,在它所在的大括号结束时被析构,而不是在函数末尾被析构。


我们可以总结出一个规律:每次调用析构函数时总是析构最近被构造的、且没有被析构的对象。也就是:先被构造的对象后被析构,析构顺序恰与构造顺序相反。熟悉数据结构的童鞋应该可以看出这就是一种栈的模型。



二、基类、派生类和成员变量(子对象类)

如果一个类继承了很多类,它又有很多成员对象,那么其构造函数的执行是极为复杂的。我们用代码举例子。

#include 

using namespace std;

// 定义两个基类
class base1
{
public:
    base1()
    {
        cout << "+ Constructor of base1n";
    }
    ~base1()
    {
        cout << "- Destructor of base1n";
    }
};

class base2
{
public:
    base2()
    {
        cout << "+ Constructor of base2n";
    }
    ~base2()
    {
        cout << "- Destructor of base2n";
    }
};

// 定义两个子对象类
class Member1
{
public:
    Member1()
    {
        cout << "+ Constructor of Member1n";
    }
    ~Member1()
    {
        cout << "- Destructor of Member1n";
    }
};

class Member2
{
public:
    Member2()
    {
        cout << "+ Constructor of Member2n";
    }
    ~Member2()
    {
        cout << "- Destructor of Member2n";
    }
};

// 定义派生类
class Derived: public base2, public base1
// 先继承base2,再继承base1
{
public:
// 先定义一个Member2的对象,再定义一个Member1的对象
    Member2 m2;
    Member1 m1;
    // 定义派生类的构造函数
    Derived(): m1(), m2(), base1(), base2()
    // 与定义时相反,按照Member1、Member2、base1、base2初始化对象
    {
        cout << "+ Constructor of Derivedn";
    }
    // 定义派生类的析构函数
    ~Derived()
    {
        cout << "- Destructor of Derivedn";
    }
};

int main()
{
    Derived d;
    return 0;
}

执行结果:

+ Constructor of base2
+ Constructor of base1
+ Constructor of Member2
+ Constructor of Member1
+ Constructor of Derived
- Destructor of Derived
- Destructor of Member1
- Destructor of Member2
- Destructor of base1
- Destructor of base2

结论:

    先调用基类的构造函数再调用子对象类(成员变量)的构造函数最后调用派生类的构造函数调用顺序与派生类构造函数冒号后面给出的初始化列表(Derived(): m1(), m2(), base1(), base2())没有任何关系,按照继承的顺序和变量再类里面定义的顺序进行初始化。 先继承base2,就先构造base2。先定义m2,就先构造m2。析构函数调用顺序仍然与构造函数构造顺序相反。

(这一段有点绕,可以暂停思考一下)



那么我们岂不是可以把两部分结合一下?出一道毒瘤C++题?
例 写出下面的程序的输出。

#include 

using namespace std;

class base1
{
public:
    base1()
    {
        cout << "+ Constructor of base1n";
    }
    ~base1()
    {
        cout << "- Destructor of base1n";
    }
};

class base2
{
public:
    base2()
    {
        cout << "+ Constructor of base2n";
    }
    ~base2()
    {
        cout << "- Destructor of base2n";
    }
};

class Member1
{
public:
    Member1()
    {
        cout << "+ Constructor of Member1n";
    }
    ~Member1()
    {
        cout << "- Destructor of Member1n";
    }
};

class Member2
{
public:
    Member2()
    {
        cout << "+ Constructor of Member2n";
    }
    ~Member2()
    {
        cout << "- Destructor of Member2n";
    }
};

class Derived: public base2, public base1
{
public:
    Member2 m2;
    Member1 m1;
    Derived(): m1(), m2(), base1(), base2()
    {
        cout << "+ Constructor of Derivedn";
    }
    ~Derived()
    {
        cout << "- Destructor of Derivedn";
    }
};

Derived global;

void function()
{
    static Member1 m1;
    {
        base2 b2;
        base1 b1;
    }
    static Member2 m2;
}

int main()
{
    for(int i = 1; i < 4; ++i) function();
    return 0;
}

答案:

+ Constructor of base2
+ Constructor of base1
+ Constructor of Member2
+ Constructor of Member1
+ Constructor of Derived
+ Constructor of Member1
+ Constructor of base2
+ Constructor of base1
- Destructor of base1
- Destructor of base2
+ Constructor of Member2
+ Constructor of base2
+ Constructor of base1
- Destructor of base1
- Destructor of base2
+ Constructor of base2
+ Constructor of base1
- Destructor of base1
- Destructor of base2
- Destructor of Member2
- Destructor of Member1
- Destructor of Derived
- Destructor of Member1
- Destructor of Member2
- Destructor of base1
- Destructor of base2

(但愿我的C++期末考试不要出这种毒瘤题……)



参考:https://en.cppreference.com/w/cpp/language/constructor

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/743500.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号