C++幕后故事(二)--编译器合成默认的构造函数

image

编译器合成缺省(无参)的构造函数

1.先考虑一个问题为什么c++中有构造函数?

这个可以从语言设计的角度来看这个问题。体现一个实例化的对象生命周期的完整性,一个对象在初始化的时候,让使用者有机会做些额外的初始化操作。同样,一个对象是消亡的时候,也要使用者有机会去释放资源。举个例子:吃饭前先洗手(构造函数),吃完饭在擦嘴(析构函数),这是个好习惯。但是你不洗手,不擦嘴,也没关系,只是这不是个好习惯而已(偶尔造成细菌感染,程序异常)。

2.编译器何时合成缺省构造函数?

好习惯的养成是一个持续的过程,所以有时候编译器会偷偷的帮你洗手擦嘴。

A.间接的含有构造函数,比如类成员变量含有构造函数,或者是继承的父类有构造函数。

B.直接或者间接的含有virtual function,比如自己含有virtual function或者是类成员变量,或者是继承的父类含有。

C.出现虚继承的现象。

image

3.那么构造函数的兄弟拷贝构造?

拷贝构造和缺省的构造也是同样的A,B,C情况下,编译器会合成默认的拷贝构造。当然编译器生成的拷贝构造就是浅拷贝。

4.那么析构函数呢?

编译器不关心你有没有写析构函数,编译器都会主动生成析构函数。在析构的时候,编译会首先调用你写的析构函数,然后在调用编译器的析构函数,这个过程对于程序员来说是透明的。从反汇编的角度看,编译器会生成一个析构函数,但是这个析构函数没有实质作用,也没有生成汇编代码

// 没有析构函数的类
class NoDctorClass
{
public:
    int m_a;
};

// 测试编译器合成析构函数
void test_compiler_geneator_def_dctor()
{
    NoDctorClass no_dctor_class;
    // 调用析构函数
    no_dctor_class.~NoDctorClass();
    no_dctor_class.m_a = 0;
}
// 转到反汇编的代码
//    NoDctorClass no_dctor_class;
//    调用析构函数,这里没有生成任何的反汇编代码
//    no_dctor_class.~NoDctorClass();
//    no_dctor_class.m_a = 0;
012AA9FE  mov         dword ptr [no_dctor_class],0  

5.那么编译器为什么帮你合成(拷贝)构造函数?

> 简单一句话,编译器需要插入一些额外的初始化代码,来完成一些语言特性。

A.间接的含有缺省构造函数,这时候编译器发现你自己没有写构造函数,但是你又间接含有构造函数。编译器这时候有两种做法,一种是心想算了吧我帮你偷偷的生成一个吧,还有一种做法就是编译器报语法错,让程序员自己解决。所以编译器厂商一商量觉得还是对程序员友好一点,用第一种做法吧。反过来想,如果一门开发语言对程序员不友好,可以说是它的生存期将会非常短。

B.直接或者间接的含有virtual function,因为含有virtual function,为了支持多态的特性,那么每个实例对象都会生成指向虚函数表的指针。但是这个指针在什么时候初始化呢?自然是(拷贝)构造函数里面。但是你自己没有写,编译器只好累一点生成(拷贝)构造函数。

C.虚继承情况,为了避免子类中含有多余的成员变量,对象在实例化的时候会生成指向虚基类表的指针。自然就会联想到在(拷贝)构造函数的时候生成,同样你自己没有写,编译器管家来替你写。

6.如何验证编译器合成了(拷贝)构造函数?

1.在你的菜单栏找到如下控制台 ![image](http://tc-test.nos-eastchina1.126.net/2020-01/02/15-03-58-411.png)

2.cd到编译生成目录下,找到你生成的obj文件,比如我生成main.obj。

3.执行 dumpbin.exe /all main.obj >> main.txt,将文件格式翻译为COFF。
image

4.查看main.txt 找到,如果所示找到1所对应的函数,下面的2就是编译器准备插入的代码,可以看到插入了MatrixA的构造函数。同理,可以验证其他的情况。

image

7.知道编译器在何时合成构造函数那么又怎么样?

1.知道编译器背着我们做了那些小动作,让我们可以更加了解这门语言背后实现的细节,做到胸中自有丘壑。

2.尽量不要依赖编译器的操作。

3.要站在编译器的角度去看问题。

8.总结:

> 构造函数和拷贝构造是你的左膀右臂,建议还是得要好好的利用。尽管有时候你不承认,但是编译器这个管家还是会偷偷摸摸的给你装个左膀右臂。如果说构造函数是你的左膀右臂,那么析构函数就是你的一把强有力的武器用于保护自己,在对象生命结束之后能够确保资源的正常释放。

9.代码示例:


/****************************************************************************
**
** Copyright (C) 2019 635672377@qq.com
** All rights reserved.
**
****************************************************************************/

/*
    测试编译器在何种情况会合成默认的构造函数
    同理可证:在何种情况下编译器会合成默认的拷贝构造函数
*/

#ifndef default_constrcuctor_h
#define default_constrcuctor_h

#include <iostream>

using std::cout;
using std::endl;

namespace defualt_constructor
{

// 含有缺省的构造函数成员类对象
// #define has_default_ctor_member 1

// 继承含有缺省的构造函数
// #define has_inherit_ctor_base 1

// 含有虚函数成员函数
// #define has_virtual_function 1

// 函数virtual函数的成员类对象
// #define has_virtual_func_member 1

// 父类含有虚函数
// #define has_inherit_virtual_func_base 1

// MatrixC, MatrixB 虚继承MatrixD
// #define has_virtual_inherit_base 1

// 类成员变量含有copy ctor
// #define has_default_copy_ctor_member 1

// 父类含有拷贝构造函数
#define has_inherit_copy_ctor_base 1

// 含有虚函数成员函数
// #define has_virtual_function_copy_ctor 1

// 类对象成员含有虚函数
// #define has_virtual_function_copy_ctor_member 1

// 父类函数虚函数
// #define has_inherit_virtual_function_copy_ctor_base 1

// 虚继承copy ctor
// #define has_virtual_inherit_function_copy_ctor_base 1

class MatrixD
{
};

#ifdef  has_virtual_inherit_base
class MatrixC : virtual public MatrixD
#elif has_virtual_inherit_function_copy_ctor_base
class MatrixC : virtual public MatrixD
#else
class MatrixC
#endif // has_virtual_inherit_base
{
public: 
#ifdef has_inherit_ctor_base
    MatrixC() { cout << "MatrixC" << endl; }
#elif has_inherit_copy_ctor_base
    MatrixC() {}
    MatrixC(const MatrixC &rhs) { cout << "MatrixC copy ctor" << endl; }
#elif has_inherit_virtual_function_copy_ctor_base
    virtual void VirFun() {}
#elif has_inherit_virtual_func_base
    virtual void VirFun() {}
#endif // has_virtual_inherit_base
};

#ifdef has_virtual_inherit_base
class MatrixB : virtual public MatrixD
#elif has_virtual_inherit_function_copy_ctor_base
class MatrixB : virtual public MatrixD
#else
class MatrixB
#endif // has_virtual_inherit_base
{
public:
#ifdef has_default_ctor_member
    MatrixB() { cout << "MatrixB" << endl; }
#elif has_virtual_func_member
    virtual void VirMatrixB() { cout << "virtual MatrixB" << endl; }
#elif has_default_copy_ctor_member
    MatrixB() {}
    MatrixB(const MatrixB &rhs) { cout << "copy ctor MatrixB" << endl; }
#elif has_virtual_function_copy_ctor_member
    virtual void VirMatrixB() { cout << "virtual MatrixB" << endl; }
#endif // has_default_ctor_member

    int m_high;
    int m_width;
};

#ifdef has_default_ctor_member
class MatrixA
#elif has_virtual_function
class MatrixA
#elif has_inherit_ctor_base
class MatrixA : public MatrixC
#elif has_inherit_copy_ctor_base
class MatrixA : public MatrixC
#elif has_virtual_func_member
class MatrixA
#elif has_virtual_inherit_base
class MatrixA : public MatrixB, public MatrixC
#elif has_inherit_virtual_func_base 
class MatrixA: public MatrixC
#elif has_inherit_virtual_function_copy_ctor_base
class MatrixA : public MatrixC
#elif has_virtual_inherit_function_copy_ctor_base
class MatrixA : public MatrixB, public MatrixC
#else
class MatrixA
#endif // has_default_ctor_member
{
public:
    int m_age;
    int m_score;

#ifdef has_default_ctor_member
    MatrixB matrixB;
#elif has_virtual_function
    virtual void VirFunc() { cout << "virtual function" << endl; }
#elif has_virtual_func_member
    MatrixB matrixB;
#elif has_default_copy_ctor_member
    MatrixA() {}
    MatrixB matrixB;
#elif has_inherit_copy_ctor_base
    MatrixA() {}
#elif has_virtual_function_copy_ctor
    virtual void VirFunc() { cout << "virtual function" << endl; }
#elif has_virtual_function_copy_ctor_member
    MatrixB matrixB;
#endif // has_default_ctor_member

};

void test_compiler_generator_def_ctor()
{
    //用dumpbin把.obj文件内容导出成可查看文件my.txt,
    // 这个my.txt格式,一般被认为是COFF:通用对象文件格式(Common Object File Format);

    MatrixA matrix;
    matrix.m_age = 0;

    // 编译器会在哪些情况下合成默认的构造函数?
    // 1.包含一个类成员变量,此成员变量含有默认的缺省构造函数。此时编译器就会
    // 生成默认的构造函数。在这个合成的构造函数中插入代码调用成员变量的构造函数
    // 2.继承的父类含有缺省的构造函数,此时编译器也会构造默认的构造函数,在子类合成的构造
    // 函数中插入代码,调用父类的缺省构造
    // 3.包含虚函数。不管是子类,父类,还是包含的成员类对象(不包含任何构造函数),只要
    // 包含了virtual function,编译器都会合成默认的构造函数
    // 4.含有虚继承现象,grandfather, parent(虚继承grand),child再继承,
    // 为了在构造函数中生成vbtable,虚基类表

    // 同样的道理,copy constrcutor也和constructor也是在同样的情况下,编译器会合成默认
    // 的构造函数

    // A.含有默认的构造函数
    //  1.父类有默认的构造函数
    //  2.包含类对象的成员变量含有默认构造函数

    // B.虚函数
    //  1.不管是自己包含虚函数,还是类成员变量有虚函数,还是父类中虚函数

    // C.虚继承
    //  1.编译器为了在插入vbtable 虚基类表
}

void test_compiler_generator_def_copy_ctor()
{
    MatrixA ma;

    MatrixA mb = ma;
    // 编译器合成默认copy ctor时机和ctor是一样的
}

}
#endif // default_constrctor_h

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 635672377@qq.com

文章标题:C++幕后故事(二)--编译器合成默认的构造函数

文章字数:2.3k

本文作者:刘世雄

发布时间:2019-11-14, 14:55:38

最后更新:2020-07-30, 09:01:51

原始链接:http://lsxcpp.com/2019/11/14/C-%E5%B9%95%E5%90%8E%E6%95%85%E4%BA%8B%EF%BC%88%E4%BA%8C%EF%BC%89-%E7%BC%96%E8%AF%91%E5%99%A8%E5%90%88%E6%88%90%E9%BB%98%E8%AE%A4%E7%9A%84%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录