0.目录

1.类成员的初始化

2.类中的const成员

3.对象的构造顺序

4.对象的析构顺序

5.小结

1.类成员的初始化

类中是否可以定义const成员?

下面的类定义是否合法?如果合法,ci的值是什么,存储在哪里?



(会报错)

C++中提供了初始化列表对成员变量进行初始化

语法规则:

注意事项:

  • 成员的初始化顺序与成员的声明顺序相同
  • 成员的初始化顺序与初始化列表中的位置无关
  • 初始化列表先于构造函数的函数体执行

证明成员的初始化顺序与成员的声明顺序相同,而与初始化列表中的位置无关:

#include <stdio.h>

class Value
{
private:
int mi;
public:
Value(int i)
{
printf("i = %d\n", i);
mi = i;
}
int getI() { return mi; }
}; class Test
{
private:
Value m2;
Value m3;
Value m1;
public:
Test() : m1(1), m2(2), m3(3)
{
printf("Test::Test()\n");
}
}; int main()
{
Test t; return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
i = 2
i = 3
i = 1
Test::Test()

2.类中的const成员

类中的const成员:

  • 类中的const成员会被分配空间的
  • 类中的const成员的本质是只读变量
  • 类中的const成员只能在初始化列表中指定初始化

编译器无法直接得到const成员的初始值,因此无法进入符号表成为真正意义上的常量。

证明类中的const成员的本质是只读变量:

#include <stdio.h>

class Value
{
private:
int mi;
public:
Value(int i)
{
printf("i = %d\n", i);
mi = i;
}
int getI()
{
return mi;
}
}; class Test
{
private:
const int ci;
Value m2;
Value m3;
Value m1;
public:
Test() : m1(1), m2(2), m3(3), ci(100)
{
printf("Test::Test()\n");
}
int getCI()
{
return ci;
}
int setCI(int v)
{
int* p = const_cast<int*>(&ci); *p = v;
}
}; int main()
{
Test t; printf("t.ci = %d\n", t.getCI()); t.setCI(10); printf("t.ci = %d\n", t.getCI()); return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
i = 2
i = 3
i = 1
Test::Test()
t.ci = 100
t.ci = 10

初始化与赋值不同:

  • 初始化:对正在创建的对象进行初值设置
  • 赋值:对已经存在的对象进行值设置

3.对象的构造顺序

3.1 局部对象的构造顺序

对于局部对象——当程序执行流到达对象的定义语句时进行构造

下面程序中的对象构造顺序是什么?

示例:

#include <stdio.h>

class Test
{
private:
int mi;
public:
Test(int i)
{
mi = i;
printf("Test(int i): %d\n", mi);
}
Test(const Test& obj)
{
mi = obj.mi;
printf("Test(const Test& obj): %d\n", mi);
}
}; int main()
{
int i = 0;
Test a1 = i; while( i < 3 )
{
Test a2 = ++i;
} if( i < 4 )
{
Test a = a1;
}
else
{
Test a(100);
} return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
Test(int i): 0
Test(int i): 1
Test(int i): 2
Test(int i): 3
Test(const Test& obj): 0

以下程序改变了程序的执行流,有严重的bug,但是不同的编译器有不同的处理结果,有些编译器不会报错,一定要注意这种问题!

#include <stdio.h>

class Test
{
private:
int mi;
public:
Test(int i)
{
mi = i;
printf("Test(int i): %d\n", mi);
}
Test(const Test& obj)
{
mi = obj.mi;
printf("Test(const Test& obj): %d\n", mi);
}
int getMi()
{
return mi;
}
}; int main()
{
int i = 0;
Test a1 = i; // Test(int i): 0 while( i < 3 )
{
Test a2 = ++i; // Test(int i): 1, 2, 3
}
goto End;
Test a(100);
End:
printf("a.mi = %d\n", a.getMi());
return 0;
}

3.2 堆对象的构造顺序

对于堆对象:

  • 当程序执行流到达new语句时创建对象
  • 使用new创建对象将自动触发构造函数的调用

下面程序中的对象构造顺序是什么?

示例:

#include <stdio.h>

class Test
{
private:
int mi;
public:
Test(int i)
{
mi = i;
printf("Test(int i): %d\n", mi);
}
Test(const Test& obj)
{
mi = obj.mi;
printf("Test(const Test& obj): %d\n", mi);
}
int getMi()
{
return mi;
}
}; int main()
{
int i = 0;
Test* a1 = new Test(i); // Test(int i): 0 while( ++i < 10 )
if( i % 2 )
new Test(i); // Test(int i): 1, 3, 5, 7, 9 if( i < 4 )
new Test(*a1);
else
new Test(100); // Test(int i): 100 return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
Test(int i): 0
Test(int i): 1
Test(int i): 3
Test(int i): 5
Test(int i): 7
Test(int i): 9
Test(int i): 100

3.3 全局对象的构造顺序

对于全局对象:

  • 对象的构造顺序是不确定的
  • 不同的编译器使用不同的规则确定构造顺序

(要避开全局对象之间的相互依赖!)

4.对象的析构顺序

单个对象创建时析构函数的调用顺序——析构函数与对应的构造函数的调用顺序相反:

  1. 调用父类的构造过程
  2. 调用成员变量的构造函数(调用顺序与声明顺序相同)
  3. 调用类自身的构造函数

多个对象析构时——析构顺序与构造顺序相反:

  • 对于栈对象和全局对象,类似于入栈与出栈的顺序,最后构造的对象被最先析构!!
  • 堆对象的析构发生在使用delete的时候,与delete的使用顺序相关!!

5.小结

  • 类中可以使用初始化列表对成员进行初始化
  • 初始化列表先于构造函数体执行
  • 类中可以定义const成员变量
  • const成员变量必须在初始化列表中指定初值
  • const成员变量为只读变量
  • 局部对象的构造顺序依赖于程序的执行流
  • 堆对象的构造顺序依赖于new的使用顺序
  • 全局对象的构造顺序是不确定的
  • 对象的析构顺序与构造顺序相反

C++解析(12):初始化列表与对象构造顺序、析构顺序的更多相关文章

  1. C++浅析——继承类中构造和析构顺序

    先看测试代码,CTEST 继承自CBase,并包含一个CMember成员对象: static int nIndex = 1; class CMember { public: CMember() { p ...

  2. C++ //继承中构造和析构顺序

    1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 class Base 6 { 7 pu ...

  3. Java内存结构、类的初始化、及对象构造过程

    概述 网上关于该题目的文章已经很多,我觉得把它们几个关联起来讲可能更好理解一下.与其它语言一样,它在执行我们写的程序前要先分配内存空间,以便于存放代码.数据:程序的执行过程其实依然是代码的执行及数据的 ...

  4. 再探Delphi2010 Class的构造和析构顺序

    发了上一篇博客.盒子上有朋友认为Class的构造和析构延迟加载.是在Unit的初始化后调用的Class的构造.在Unit的反初始化前调用的Class的析构函数. 为了证明一下我又做了个试验 unit ...

  5. C++语法小记---继承中的构造和析构顺序

    继承中构造和析构的顺序 先父母,后客人,最后自己 静态变量和全局变量在最开始 析构和构造的顺序完全相反 #include <iostream> #include <string> ...

  6. C++ 类成员的构造和析构顺序

    我想对面向对象有了解的童鞋应该不会对类和对象感到陌生吧 ! 对象并不是突然建立起来的,创建对象必须时必须同时创建父类以及包含于其中的对象.C++遵循如下的创建顺序: (1)如果某个类具体基类,执行基类 ...

  7. C++初始化列表各情况分析

    今天回顾了下C++初始化列表的知识,接下来我对这一知识作一总结. 我们在定义了一个类的时候,需要对类的成员进行初始化.关于初始化,有两种方法,一种在初始化列表中进行,另一种就是在构造函数中进行,对于这 ...

  8. C++中初始化列表的使用(总结)

    原文链接 https://www.cnblogs.com/dishengAndziyu/p/10906081.html 参考链接:https://www.cnblogs.com/laiqun/p/57 ...

  9. C++类构造函数初始化列表

    C++类构造函数初始化列表 构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式.例如: class CExample {public:     ...

随机推荐

  1. Lite OS学习之事件EVENT

    1. Lite OS的事件EVENT,就是一个任务向另外一个任务通知事件的,不能数据传输.看下有的函数,实际比较复杂 2. 具体还是看编程,先全局结构体整个事件变量 /*事件控制结构体*/ EVENT ...

  2. Awesome TensorFlow

    Awesome TensorFlow  A curated list of awesome TensorFlow experiments, libraries, and projects. Inspi ...

  3. replace与replaceAll的区别

    这两者有些人很容易搞混,因此我在这里详细讲述下. replace的参数是char和CharSequence,即可以支持字符的替换,也支持字符串的替换(CharSequence即字符串序列的意思,说白了 ...

  4. 高能福利 |"荐"者有份,有"福"同享

    WeTest 导读 越来越多的开发者加入WeTest大家庭了,感谢大家一直以来的支持,WeTest又有新一步“大福利”赠送了! 即日起,参加WeTest用户推荐有礼活动,推荐者和被推荐者皆可获得福利. ...

  5. Egret入门(三)--创建HelloWorld项目(4.0-使用Egret Wing)

    准备 编辑器: Egret Wing3(4.0.3) 需要下载安装 语言: TepyScript(JS的超集,参考手册http://bbs.egret.com/thread-1441-1-1.html ...

  6. ideal快捷键

    百度一搜索,发现很多快捷键说明,我但是有些说得不对的,我列出来的这些快捷键,有一部分是需要你百度好久,甚至百度一上午才能搜索出来的,并且戴着老花镜.这样的话,在实际工作者,对于初级程序员来说,成本太高 ...

  7. File Transfer(并查集)

    题目大意:将多个电脑通过网线连接起来,不断查询2台电脑之间是否连通. 问题来源:中国大学mooc 05-树8 File Transfer (25 分) We have a network of com ...

  8. js判断PC端 移动端 并跳转到对应页面

    一.PC端跳转到移动端 html页面: <script>var webroot="/",catid="{$catid}",murl="m/ ...

  9. Python基础入门(迭代器和生成器)

    1 Python迭代器 迭代器是一个可以记住遍历的位置的对象. 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束. 迭代器只能往前不会后退. 迭代器有两个基本的方法:iter() 和 ...

  10. Oracle和MySQL在使用上的区别

    1.  Oracle是大型数据库而MySQL是中小型数据库,MySQL是开源的而Oracle的价格非常高. 2.  Oracle支持大并发,大访问量. 3.  安装所用的空间差别也是很大,MySQL安 ...