如果一个类没有自己的构造函数,编译器会在需要的时候为其合成一个出来,俗称:合成默认构造函数。但是请注意是在需要的时候,并不是所有情况。

请看下面代码:

 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++对象模型)的更多相关文章

  1. C++构造函数语义学(二)(基于C++对象模型)

    带有虚函数的情况. 下面情况编译器也会在需要的时候为其合成. 1.如果一个类自己声明为虚函数. 1 #include<iostream> 2 using namespace std; 3 ...

  2. C++构造函数语义学(三)(基于C++对象模型)

    带有虚基类的情况. 1 #include<iostream> 2 using namespace std; 3 class X 4 { 5 public: 6 int i; 7 }; 8 ...

  3. 构造函数语义学——Copy Constructor 篇

    构造函数语义学--Copy Constructor 篇 本文主要介绍<深度探索 C++对象模型>之<构造函数语义学>中的 Copy Constructor 构造函数的调用时机 ...

  4. 构造函数语义学之Copy Constructor构建操作(2)

    二.详述条件 3 和 4 那么好,我又要问大家了,条件1 和 2比较容易理解.因为member object或 base class 含有copy constructor.那么member objec ...

  5. 构造函数语义学——Default Constructor篇

    构造函数语义学--Default Constructor 篇 这一章原书主要分析了:编译器关于对象构造过程的干涉,即在对象构造这个过程中,编译器到底在背后做了什么 这一章的重点在于 default c ...

  6. 《深度探索c++对象模型》chapter2 构造函数语义学

    关于c++,最常听到的一个抱怨是,编译器背着程序员做了太多事情,conversion运算符是最常被引用的一个例子:jerry schwarz,iostream函数库的建筑师,就曾经说过一个故事,他说他 ...

  7. 【C++】深度探索C++对象模型读书笔记--构造函数语义学(The Semantics of constructors)(四)

    成员们的初始化队伍(member Initia 有四种情况必须使用member initialization list: 1. 当初始化一个reference member时: 2. 当初始化一个co ...

  8. 构造函数语义学之Copy Constructor构建操作(1)

    一.Copy Constructor的构建操作 就像 default constructor 一样,如果class没有申明一个 copy constructor,就会隐含的声明或隐含的定义一个.生成的 ...

  9. 构造函数语义学之Default Constructor构建操作

    一.Default Constructor的构建操作 首先大家要走出两个误区: 1).任何class如果没有定义default constructor,就会被合成一个来. 2).便以其合成出来的def ...

随机推荐

  1. centos使用docker安装mysql5.7

    搜索镜像 docker search mysql 拉取镜像 docker pull mysql:5.7 启动mysql镜像 docker run -p 3306:3306 --name mysql - ...

  2. maven中pom文件中scope的作用

    Dependency Scope  <dependency>中还引入了<scope>,它主要管理依赖的部署.目前<scope>可以使用5个值: compile 默认 ...

  3. cmake之指定clang(++)编译器为默认编译器

    1.说明 本文演示环境的cmake版本3.18 clang是自己源码安装的,非获取已经编译好的binary 2.代码 2.1 添加下面的代码到CMakeLists.txt中,且放到 project语句 ...

  4. 【LeetCode】1165. Single-Row Keyboard 解题报告(C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典 日期 题目地址:https://leetcode ...

  5. 【九度OJ】题目1064:反序数 解题报告

    [九度OJ]题目1064:反序数 解题报告 标签(空格分隔): 九度OJ 原题地址:http://ac.jobdu.com/problem.php?pid=1064 题目描述: 设N是一个四位数,它的 ...

  6. 【LeetCode】504. Base 7 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 内建库 BigInteger类 逐位计算 倍数相加 ...

  7. 【LeetCode】926. Flip String to Monotone Increasing 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 Prefix计算 动态规划 参考资料 日期 题目地址 ...

  8. 【LeetCode】662. Maximum Width of Binary Tree 解题报告(Python)

    [LeetCode]662. Maximum Width of Binary Tree 解题报告(Python) 标签(空格分隔): LeetCode 题目地址:https://leetcode.co ...

  9. Universal adversarial perturbations

    目录 概 主要内容 算法 实验部分 实验1 实验2 实验3 代码 Moosavidezfooli S, Fawzi A, Fawzi O, et al. Universal Adversarial P ...

  10. docker学习:docker---centos安装

    查看目标镜像 docker search centos 拉取镜像 docker pull centos 查看镜像 docker images 启动镜像 docker run -itd --privil ...