编译器合成缺省(无参)的构造函数
1.先考虑一个问题为什么c++中有构造函数?
这个可以从语言设计的角度来看这个问题。体现一个实例化的对象生命周期的完整性,一个对象在初始化的时候,让使用者有机会做些额外的初始化操作。同样,一个对象是消亡的时候,也要使用者有机会去释放资源。举个例子:吃饭前先洗手(构造函数),吃完饭在擦嘴(析构函数),这是个好习惯。但是你不洗手,不擦嘴,也没关系,只是这不是个好习惯而已(偶尔造成细菌感染,程序异常)。
2.编译器何时合成缺省构造函数?
好习惯的养成是一个持续的过程,所以有时候编译器会偷偷的帮你洗手擦嘴。
A.间接的含有构造函数,比如类成员变量含有构造函数,或者是继承的父类有构造函数。
B.直接或者间接的含有virtual function,比如自己含有virtual function或者是类成员变量,或者是继承的父类含有。
C.出现虚继承的现象。
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.在你的菜单栏找到如下控制台
2.cd到编译生成目录下,找到你生成的obj文件,比如我生成main.obj。
3.执行 dumpbin.exe /all main.obj >> main.txt,将文件格式翻译为COFF。
4.查看main.txt 找到,如果所示找到1所对应的函数,下面的2就是编译器准备插入的代码,可以看到插入了MatrixA的构造函数。同理,可以验证其他的情况。
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