c++在对象的构造时做了什么手脚?——《深度探索c++对象模型》阅读笔记之一

Posted 低熵随想

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++在对象的构造时做了什么手脚?——《深度探索c++对象模型》阅读笔记之一相关的知识,希望对你有一定的参考价值。

前言

  《深度探索c++对象模型》是《c++ primer》的作者Lippman所写的一本书,Lippman从自己参与开发cfront的经历出发,深度剖析了c++中对象的相关特性是如何被实现的。

  这本书相对其他的各种c++特性解读,用法,最佳实践的书来说,相对艰深晦涩,其面对的也不是刚入门的C++ 学习者,而是想要了解C++在幕后做了什么的人。

  这本书的作用应当是消除程序员脑海中的不确定性,揭开C++对象表面的魔法。一个对象,其定义, 创建,调用方法,析构等等,编译器有为你做了什么,又没做什么,如果想要真正的了解C++对象的工作机理,阅读这本书是大有裨益的。

  本笔记是为了记录一些阅读中的知识点而设立,同时也是消化知识的一个过程。

 

以下内容是对书中第二章《The semantics of constructors》的消化梳理

 

1. 默认构造函数

对于简单的class中,我们经常略过对默认构造函数的声明和定义,编译器会照顾好一切,我们只要调用就行。

但是如果说究其根本,我们必须问,编译器真的为每个Class创建了默认构造函数吗?

为何这么问,因为抛去所有的对象特性的外衣,它仍然是一个函数,真的需要对每一个实例都调用一次这个函数吗?

假如说我依照c语言中的习惯定义了以下这个sturct:

1 struct point{
2     int x;
3     int y; 
4 };

我只想像在c语言一样,将这个只看做是几个基本类型变量的组合来使用,那么每次创建一个point实例,都要调用一次构造函数,这也太疯狂了!

c++的设计者们也是这样想的,有些时候,默认构造函数是完全不需要的,只需要分配好内存即可,这时我们说这是一个trivial的默认构造函数,只是观念上存在,但是编译器并不会生成相关代码。

编译器只在以下特定的几种情况下会合成一个默认构造函数

1.1 成员中有拥有默认构造函数的对象

1 class bar{
2 //有默认构造函数
3 };
4 
5 class foo {
6    bar member; 
7 };

当一个类中的成员是一个拥有默认构造函数的类时,编译器就无法袖手旁观了,它必须给这个类加上一个合成的默认构造函数,以调用其成员的默认构造函数。

当有多个这样的成员类时,合成的默认构造函数会按照声明顺序进行调用成员的默认构造函数。

如果说我们已经自己定义了默认构造函数呢? 那么编译器会向我们定义的函数前插入代码来初始化成员对象

1.2 继承自有默认构造函数的基类

1 class base{// 有构造函数};
2 class derived :public base{};

这种情况是下,因为继承的性质使然,派生类必然需要调用父类的默认构造函数,所以编译器一定要合成一个默认构造函数

1.3 带虚函数的类

c++中动态多态的展现,需要通过虚函数,其实现是通过虚函数表来做到的。存在虚函数的类中,在实例化时加入一个指向虚函数表的指针,做这件事必须通过构造函数来实现。

 

1.4 继承自虚基类的类

虚基类也是动态的,在编译期无法确定其内存位置,所以必然也需要有指针指向,在对象创建时,对这个指针进行操作来确定虚基类的内存位置。 那么为了实现这个功能,必然需要合成默认构造函数

 

2. 拷贝构造函数

对于拷贝构造函数,不少人会有迷糊的感觉,觉得编译器会给每一个类合成一个,但是实际远不是这样的,

因为对于部分情况,拷贝构造函数是没有任何必要的,只需要进行所谓的 bitwise copy就行,即把对象对应的二进制内容原封不动的复制一份就行

但在一些情况下,编译器会压抑bitwise copy, 为类合成一份拷贝构造函数

 

2.1  当成员有拥有一个拷贝构造函数时

同默认构造函数类似,一个成员拥有拷贝构造函数(无论时被编译器合成的,还是自己显式定义的)时,该类就不能进行bitwise copy了,需要合成拷贝构造函数。

否则就无法调用成员的拷贝构造函数

 

2.2  继承自拥有拷贝构造函数的基类

和默认构造函数类似,拷贝时必须调用基类的拷贝构造函数,所以必然需要合成

 

2.3 类拥有虚函数时

在这种情况,拷贝构造函数需要设置虚函数表,不能理解的话考虑用一个派生类对象初始化基类对象,这时必然需要设置成员中的虚函数表指针重新指向基类的虚函数表。

 

2.4 类派生自虚基类时

和默认构造函数类似,需要保证其虚基类指针的正确设定,必然需要合成拷贝构造函数

 

本章中还谈论了 NRV优化其利弊和形式,以及面试官很喜欢的初始化列表的问题。这些暂时就不写了。

以上是关于c++在对象的构造时做了什么手脚?——《深度探索c++对象模型》阅读笔记之一的主要内容,如果未能解决你的问题,请参考以下文章

20200407-cpp-object-model

深度探索C++对象模型笔记

《深度探索c++对象模型》学习笔记

深度探索C++对象模型——Function语意学

深度探索C++对象模型——Function语意学

深度探索C++对象模型