C++构造函数语义学(一)(基于C++对象模型)
如果一个类没有自己的构造函数,编译器会在需要的时候为其合成一个出来,俗称:合成默认构造函数。但是请注意是在需要的时候,并不是所有情况。
请看下面代码:
1 #include<iostream>
2 using namespace std;
3 class Foo {
4 public:
5 int val;
6 Foo *pnext;
7 };
8
9 void foo_bar()
10 {
11 Foo bar;
12 if (bar.val || bar.pnext)
13 {
14 cout << "数据被编译器初始化了" << endl;
15 }
16 }
17 int main()
18 {
19 foo_bar();
20 while (1);
21 return 0;
22 }
输出会报错:
分析:对于上述情况,编译器并不会认为是需要的,因为初始化数据成员应该是程序员的职责,而并非是编译器的职责。所以必须自己写一个构造函数出来为其数据成员进行初始化。
假设编译器认为需要的时候到了,这时候为其合成了一个默认构造函数出来,也不会对该类的数据成员进行初始化,因为这并不是编译器的职责。比如下面代码:
1 #include<iostream>
2 using namespace std;
3 class Foo {
4 public:
5 Foo(){
6 cout << "默认构造函数被调用" << endl;
7 };
8 };
9
10 class Bar {
11 public:
12 Foo foo;
13 char *str;
14 };
15 void foo_bar()
16 {
17 Bar bar;
18 if (bar.str)
19 {
20 cout << "类Bar的数据成员被编译器初始化了" << endl;
21 }
22 }
23 int main()
24 {
25 foo_bar();
26 while (1);
27 return 0;
28 }
输出:

和第一种情况比,这时候的编译器没有报错,但是编译器还是没有为数据成员进行初始化。
上面的代码引出第一种编译器会自动合成默认构造函数的情况:如果一个类中包含另一个类,对于后者,有自己定义的默认构造函数。而前者没有,那么此时编译器会为其合成一个出来,但是合成的目的仅仅只是为了调用后者的默认构造函数,对于自己类的数据成员,必须由程序员自己初始化。
所以合理的应该是这样的:
1 #include<iostream>
2 using namespace std;
3 class Foo {
4 public:
5 Foo() {
6 cout << "默认构造函数被调用" << endl;
7 };
8
9 };
10
11 class Bar {
12 public:
13 Foo foo;
14 char *str;
15 Bar()
16 {
17 //编译器自动安插代码段foo.Foo::Foo();
18 str = new char;//程序员为自己的数据成员赋值。
19 }
20
21 };
22 void foo_bar()
23 {
24 Bar bar;
25 if (bar.str)
26 {
27 cout << "类Bar的数据成员被编译器初始化了" << endl;
28 }
29 }
30 int main()
31 {
32 foo_bar();
33 while (1);
34 return 0;
35 }
输出:

分析:
对于类Bar,现在有了自己的构造函数,虽然没有显示的定义类Foo的,编译器还是为其合成了一个出来,因为编译器认为这是需要的时候,这种需要仅仅是需要调用类Foo的默认构造函数,但是对于自己的数据成员,它依然置之不顾。
如果一个类包含有多个类成员,这些类成员都有自己的构造函数(假设默认的不带参数和带参数的构造函数都存在),但是包含了多个类成员的类并没有定义自己的构造函数,编译器此时就会为其合成一个出来,为的就是调用其类成员,且调用顺序按照类成员定义的顺序来依次调用,比如以下情况:
1 #include<iostream>
2 using namespace std;
3 class Dopey {
4 public:
5 Dopey() {
6 cout << "默认构造函数Dopey()被调用" << endl;
7 }
8 };
9 class Sneezy {
10 public:
11 Sneezy(int) {
12 cout << "构造函数Sneezy(int)被调用" << endl;
13 }
14 Sneezy(){
15 cout << "默认构造函数Sneezy()被调用" << endl;
16 }
17 };
18 class Bashful {
19 public:
20 Bashful() {
21 cout << "默认构造函数Bashful()被调用" << endl;
22 }
23 };
24 class Snow_White
25 {
26 public:
27 Dopey dopey;
28 Sneezy sneezy;
29 Bashful bashful;
30 private:
31 int mumble;
32 };
33 int main()
34 {
35
36 Snow_White s;
37 while (1);
38 return 0;
39 }
输出:

如果该类为自己的数据成员定义了自己的构造函数来初始化它们,如果不明确调用父类的构造函数,编译器依旧会在该构造函数中安插代码,为了调用父类的默认构造函数(编译器调用的都是默认的版本,也就是父类中定义的都是不带参的构造函数或者带了默认参数的构造函数,其他情况则需要程序员自己显示调用),如下情况:
1 #include<iostream>
2 using namespace std;
3 class Dopey {
4 public:
5 Dopey() {
6 cout << "默认构造函数Dopey()被调用" << endl;
7 }
8 };
9 class Sneezy {
10 public:
11 Sneezy(int) {
12 cout << "构造函数Sneezy(int)被调用" << endl;
13 }
14 Sneezy(){
15 cout << "默认构造函数Sneezy()被调用" << endl;
16 }
17 };
18 class Bashful {
19 public:
20 Bashful() {
21 cout << "默认构造函数Bashful()被调用" << endl;
22 }
23 };
24 class Snow_White
25 {
26 public:
27 Dopey dopey;
28 Sneezy sneezy;
29 Bashful bashful;
30 Snow_White() :sneezy(1024)
31 {
32 /*
33 编译器安插:
34 dopey.Dopey::Dopey();
35 sneey.Sneey::Sneey(1024);
36 bashful.Bashful::bashful();
37 */
38 mumble = 2048;
39 }
40 private:
41 int mumble;
42 };
43 int main()
44 {
45 Snow_White s;
46 while (1);
47 return 0;
48 }
输出:

上述是类中含有类成员的情况,继承的情况和它类似,如果一个类继承了另外一个类,但是前者没有自己的构造函数,而后者有自己的默认构造函数,那么对于前者,编译器会在需要的时候合成一个出来,目的仅仅是为了调用后者的默认构造函数,前者的数据成员仍然需要程序源自己初始化,对于多继承同样如此,下面举出多继承的例子:
1 #include<iostream>
2 using namespace std;
3 class Dopey {
4 public:
5 Dopey() {
6 cout << "默认构造函数Dopey()被调用" << endl;
7 }
8 };
9 class Sneezy {
10 public:
11 Sneezy(int) {
12 cout << "构造函数Sneezy(int)被调用" << endl;
13 }
14 Sneezy(){
15 cout << "默认构造函数Sneezy()被调用" << endl;
16 }
17 };
18 class Bashful {
19 public:
20 Bashful(int x = 2) {
21 cout << "默认构造函数Bashful()被调用" << endl;
22 }
23 };
24 class Snow_White:public Dopey,public Sneezy,public Bashful
25 {
26 public:
27 Snow_White() :Sneezy(1024)
28 {
29 /*
30 编译器安插:
31 Dopey dopey;
32 dopey.Dopey::Dopey();
33 Sneezy sneezy;
34 sneezy.Sneey::Sneey(1024);
35 Bashful bashful;
36 bashful.Bashful::bashful();
37 */
38 mumble = 2048;
39 }
40 private:
41 int mumble;
42 };
43 int main()
44 {
45 Snow_White s;
46 while (1);
47 return 0;
48 }
输出:

C++构造函数语义学(一)(基于C++对象模型)的更多相关文章
- C++构造函数语义学(二)(基于C++对象模型)
带有虚函数的情况. 下面情况编译器也会在需要的时候为其合成. 1.如果一个类自己声明为虚函数. 1 #include<iostream> 2 using namespace std; 3 ...
- C++构造函数语义学(三)(基于C++对象模型)
带有虚基类的情况. 1 #include<iostream> 2 using namespace std; 3 class X 4 { 5 public: 6 int i; 7 }; 8 ...
- 构造函数语义学——Copy Constructor 篇
构造函数语义学--Copy Constructor 篇 本文主要介绍<深度探索 C++对象模型>之<构造函数语义学>中的 Copy Constructor 构造函数的调用时机 ...
- 构造函数语义学之Copy Constructor构建操作(2)
二.详述条件 3 和 4 那么好,我又要问大家了,条件1 和 2比较容易理解.因为member object或 base class 含有copy constructor.那么member objec ...
- 构造函数语义学——Default Constructor篇
构造函数语义学--Default Constructor 篇 这一章原书主要分析了:编译器关于对象构造过程的干涉,即在对象构造这个过程中,编译器到底在背后做了什么 这一章的重点在于 default c ...
- 《深度探索c++对象模型》chapter2 构造函数语义学
关于c++,最常听到的一个抱怨是,编译器背着程序员做了太多事情,conversion运算符是最常被引用的一个例子:jerry schwarz,iostream函数库的建筑师,就曾经说过一个故事,他说他 ...
- 【C++】深度探索C++对象模型读书笔记--构造函数语义学(The Semantics of constructors)(四)
成员们的初始化队伍(member Initia 有四种情况必须使用member initialization list: 1. 当初始化一个reference member时: 2. 当初始化一个co ...
- 构造函数语义学之Copy Constructor构建操作(1)
一.Copy Constructor的构建操作 就像 default constructor 一样,如果class没有申明一个 copy constructor,就会隐含的声明或隐含的定义一个.生成的 ...
- 构造函数语义学之Default Constructor构建操作
一.Default Constructor的构建操作 首先大家要走出两个误区: 1).任何class如果没有定义default constructor,就会被合成一个来. 2).便以其合成出来的def ...
随机推荐
- Mac下好用的“visio”之 OmniGraffle Pro
!!版权声明:本文为博主原创文章,版权归原文作者和博客园共有,谢绝任何形式的 转载!! 作者:mohist 1.官方网站:https://www.omnigroup.com/omnigraffle/ ...
- 【机器学*】k-*邻算法(kNN) 学*笔记
[机器学*]k-*邻算法(kNN) 学*笔记 标签(空格分隔): 机器学* kNN简介 kNN算法是做分类问题的.思想如下: KNN算法的思想总结一下:就是在训练集中数据和标签已知的情况下,输入测试数 ...
- 【LeetCode】99. Recover Binary Search Tree 解题报告(Python)
[LeetCode]99. Recover Binary Search Tree 解题报告(Python) 标签(空格分隔): LeetCode 题目地址:https://leetcode.com/p ...
- Java不可变类与final类
概念 Java的不可变类是指八个基础类型的包装类和String,他们的数据成员是不可变的.使用加法等操作时,其实是创建了一个新的对象. Java的final类是对类用关键字final进行修饰,说明该类 ...
- 【LeetCode】678. Valid Parenthesis String 解题报告(Python)
[LeetCode]678. Valid Parenthesis String 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人 ...
- WebRTC下 的 NAT 穿透技术
NAT的概念模型 NAT名字很准确,网络地址转换,就是替换IP报文头部的地址信息.NAT通常部署在一个组织的网络出口位置,通过将内部网络IP地址替换为出口的IP地址提供公网可达性和上层协议的连接能力. ...
- 【Java笔记】Java使用mysql包注意
注意 安装的mysql5.x版本对应 5.x版本的驱动包 安装的mysql8.x版本对应 8.x版本的驱动包 如果安装的MySQL版本和驱动包版本不符合,则Java的连接不了数据库
- Azure Data Lake(一) 在NET Core 控制台中操作 Data Lake Storage
一,引言 Azure Data Lake Storage Gen2 是一组专用于大数据分析的功能,基于 Azure Blob Storage 构建的.Data Lake Storage Gen2 包含 ...
- Python pyecharts绘制词云图
一.pyecharts绘制词云图WordCloud.add()方法简介 WordCloud.add()方法简介 add(name,attr,value, shape="circle" ...
- [opencv]zxing c++ 库的编译,安装,以及api的介绍
环境:ubuntu 16.04 1. 下载:zxing的源码 git clone https://github.com/15903016222/zxing-cpp.git2. 安装编译依赖的工具:cm ...