C/C++知识点之对象的构造顺序
从安 2019-06-05 来源 : 阅读 1013 评论 0

摘要:本篇文章主要讲述C/C++知识点之对象的构造顺序,希望阅读本篇文章以后大家有所收获,帮助大家对相关内容的理解更加深入。

本篇文章主要讲述C/C++知识点之对象的构造顺序,希望阅读本篇文章以后大家有所收获,帮助大家对相关内容的理解更加深入。

C/C++知识点之对象的构造顺序

1,C++ 中的类可以定义多个对象,那么对象构造顺序是怎样的?

    1,很多的 bug 是由对象的构造顺序造成的,虽然它不难;

    2,对象的构造往往和构造函数牵涉在一起,构造函数的函数体又可能由非常复杂的程序逻辑组成的;

    3,这样就有可能引入了一个问题,不同类的它们的构造函数中的程序逻辑也许是相互依赖的,当这种相互依赖一旦发生,那么对象的构造顺序就很可能导致程序中非常难以调试的 bug 出现;

    4,在工程中,由于对象的构造顺序而造成的软件 bug 非常之多,因此有必要梳理下对象的构造顺序;

 

2,对于局部对象:

    1,当程序执行流到达对象的定义语句时进行构造;

       1,构造时调用构造函数;

   

3,局部对象的构造顺序实例分析:

     1,代码示例:

1 #include 2 
3 class Test
4 {
5 private:
6     int mi;
7 public:
8     Test(int i)
9     {
10         mi = i;
11         printf("Test(int i): %d\n", mi);  // 打印语句对实验非常有用;
12     }
13     
14     Test(const Test& obj)
15     {
16         mi = obj.mi;
17         printf("Test(const Test& obj): %d\n", mi);
18     }
19 };
20 
21 int main()
22 {
23     int i = 0;
24     Test a1 = i;  // 第一个被构造的对象
25         
26     while( i < 3 )
27     {
28         Test a2 = ++i;  // 第二个被构造的对象;反复的被构造三次对象;
29     }
30     
31 // goto End;  // 下面的输出不会执行了;程序执行流跳过了程序调用的定义,则对象就没有被构造了;
32     if( i < 4 )  
33     {
34         Test a = a1;  // 第三个被构造的对象;Test(const Test& obj): 3
35     }
36     else
37     {
38         Test a(100);
39     }
40 // End:
41     return 0;
42 }

  2,这个实验说明:

         1,程序执行流直接和局部对象的构造顺序息息相关,如果非法的改变程序执行流,那么程序可能产生灾难性的错误,参见如下示例:

1 #include  
2  
3 class Test 
4 { 
5 private: 
6     int mi; 
7 public: 
8     Test(int i) 
9     {
10         mi = i;
11         printf("Test(int i): %d\n", mi);
12     }
13     
14     Test(const Test& obj)
15     {
16         mi = obj.mi;
17         printf("Test(const Test& obj): %d\n", mi);
18     }
19     
20     int getMi()
21     {
22         return mi;
23     }
24 };
25 
26 int main()
27 {
28     int i = 0;
29     Test a1 = i; // Test(int i): 0
30         
31     while( i < 3 )
32     {
33         Test a2 = ++i; // Test(int i): 1, 2, 3
34     }
35 goto End;       
36         Test a(100);  // 这里程序执行流跳过了对象的定义,因此 a 这个对象是没有被构造的,没有被构造意味着 a 这个对象它的状态是没有被初始化的,如果后面使用这个对象,则会产生灾难性的错误;g++ 可以帮助我们报出这个错误,但是其它编译器有可能不能,因为这不是标准,比如 Visual Studio 中的编译器;
37 End:
38     printf("a.mi = %d\n", a.getMi());  // 这里打印随机值;a 对象没有被初始化,因为构造函数没有被调用;
39     
40     return 0;
41 }

    2,这个实验说明:

           1,对象的构造如果不是显示的调用,则只发生在对象定义(运行时)之时;

           2,编译时只产生没有初始化的对象(这里很模糊,不清楚);

           3,程序执行流和程序的编译不相关,编译还是会都编译的。执行确定了构造函数的调用,而编译则是生成空间;

   

4,对于堆对象:

    1,当程序执行流到达 new 语句时创建对象;

       1,创建对象就要触发构造函数的调用;

       2,其实堆对象构造顺序也和程序执行流相关,只不过说由于引入了 new 关键字,堆对象的构造顺序比局部对象更容易确认些;

    2,使用 new 创建对象将自动触发构造函数的调用;

   

5,堆对象的构造顺序编程实验:

1 #include  
2  
3 class Test 
4 { 
5 private: 
6     int mi; 
7 public: 
8     Test(int i) 
9     {
10         mi = i;
11         printf("Test(int i): %d\n", mi);
12     }
13     
14     Test(const Test& obj)
15     {
16         mi = obj.mi;
17         printf("Test(const Test& obj): %d\n", mi);
18     }
19     
20     int getMi()
21     {
22         return mi;
23     }
24 };
25 
26 int main()
27 {
28     int i = 0;
29     Test* a1 = new Test(i); // Test(int i): 0
30         
31     while( ++i < 10 )
32         if( i % 2 )
33             new Test(i); // Test(int i): 1, 3, 5, 7, 9
34         
35     if( i < 4 )
36         new Test(*a1);
37     else
38         new Test(100); // Test(int i): 100
39         
40     return 0;
41 }

    1,堆对象的创建也会受到 goto 语句的影响,因为 goto 语句会改变程序执行流,也就可能绕过堆对象的创建;

   

6,对于全局对象:

    1,全局对象的构造顺序是不确定的;

       1,这里带来的 bug 是非常多的;

       2,C++ 标准没有定义全局对象的构造顺序;

    2,不同的编译器使用不同的规则确定构造顺序;

   

7,全局对象的构造顺序:

    1,test.h文件:

1 #ifndef _TEST_H_ 
2 #define _TEST_H_ 
3
4 #include  
5
6 class Test 
7 { 
8 public: 
9     Test(const char* s)
10     {
11         printf("%s\n", s);
12     }
13 };
14 
15 #endif

    2,t1.cpp 文件:

1 #include "test.h"
2
3 Test t1("t1");

    3,t2.cpp 文件:

1 #include "test.h"
2
3 Test t2("t2");

    4,t3.cpp 文件:

1 #include "test.h"
2
3 Test t3("t3");

 

    5,主函数:

1 #include "test.h"
2 
3 Test t4("t4");  // 全局对象的构造一定会在 main 函数之前完成,和程序执行流没有关系;main() 函数执行之前,没有程序执行流的概念,如果出现多个全局对象的时候,它们的构造顺序就不确定了;尤其是两个不同的操作系统更是不同;
4 int main()
5 {
6     Test t5("t5");
7 }

    1,在以后的开发过程中,要避免全局对象之间的相互依赖;

    2,当今的软件开发领域,尽量的不使用全局变量的,其中原因之一就是全局的变量它们的初始化顺序是不确定的,面向对象领域就变成了全局对象的构造顺序是不确定的;

    3,goto 语句也是禁用的,因为它会改变程序执行流,这样会造成局部对象构造顺序产生错乱,有可能导致程序里面局部对象构造出现问题,进而产生不可预计的灾难性错误;

   

8,小结:

    1,局部对象的构造顺序依赖于程序的执行流;

    2,堆对象的构造顺序依赖于 new 的使用顺序;

    3,全局对象的构造顺序是不确定的;

 

本文由职坐标整理发布,学习更多的相关知识,请关注职坐标IT知识库!

本文由 @从安 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程