深度探索C++对象模型(3)——对象(3)——构造函数语义

默认构造函数(缺省构造函数)分析

     即没有参数的构造函数

     传统认识认为:如果我们自己没定义任何构造函数,那么编译器就会为我们隐式自动定义 一个默认的构造函数,
我们称这种构造函数为:“合成的默认构造函数”

    结论:“合成的默认构造函数”,只有在 必要的时候,编译器才会为我们合成出来,而不是必然或者必须为我们合成出来。
    

必要的时候是什么时候?

示例代码

#include <iostream>
using namespace std;

class MATX
{
public:
	MATX() //默认构造函数
	{
		cout << "goodHAHAHAHA" << endl;
	}
};

class MBTX
{
public:
	int m_i;
	int m_j;
	void funct()
	{
		cout << "IAmVeryGood" << endl;
	}
};

int main()
{
	MBTX myb;
	return 1;
}

编译通过会生成.obj文件:

每个.cpp源文件会编译生成一个.obj(linux下 gcc -c生成.o文件),最终把很多的.obj(.o)文件链接到一起生成一个可执行。

Windows下查看.obj文件

(1)右键单击正在运行程序的文件,打开所在文件夹

(2)进入它的debug文件夹,就可以找到该文件刚才编译生成的.obj文件

(3)找到开始菜单中visual studio中的开发人员命令提示符并打开

(4)输入命令如下(我的test1文件在F:盘,所以首先进入F:盘),会在.obj的文件目录下生成test1.txt文件,存放的是.o文件的分析结果

(5)用visual studio2017查看test1.txt结果如图

我们使用快速查找验证编译器是否为MBTX生成了默认构造函数MBTX::MBTX()

编译器并没有合成,因为此时只有普通类型的变量,编译器并不会合成默认构造函数

编译器会在什么时候帮我们合成默认构造函数呢?

(1)该类MBTX没有任何构造函数,但包含一个类类型的成员ma,而该对象ma所属于的类MATX 有一个缺省的构造函数

#include <iostream>
using namespace std;

class M0TX
{
public:
	M0TX() //默认构造函数
	{
		cout << "合成了默认构造函数,调用了M0TX类" << endl;
	}
};
class MATX
{
public:
	MATX() //默认构造函数
	{
		cout << "合成了默认构造函数,调用了MATX类" << endl;
	}
};

class MBTX
{
public:
	int m_i;
	int m_j;

	M0TX m0;  //类类型成员变量
	MATX ma; //类类型成员变量


	void funct()
	{
		cout << "IAmVeryGood" << endl;
	}
};

int main()
{
	MBTX myb;
	return 1;
}

运行结果:

再使用dumpbin命令更新test1.txt,查找MBTX::MBTX()

此时编译器帮我们合成了默认构造函数,合成的目的是为了调用MATX里的默认构造函数。

换句话说:编译器合成了默认的MBTX构造函数,并且在其中 安插代码,调用M0TX,MATX中的默认构造函数;

并且注意:此时调用类类型M0TX,MATX的默认构造函数的顺序与在该类MBTX中声明的顺序相同

(2)父类带缺省构造函数,子类没有任何构造函数,那因为父类这个缺省的构造函数要被调用,所以编译器会为这个子类合成出一个默认构造函数。
     合成的目的是为了调用这个父类的构造函数。换句话说,编译器合成了默认的构造函数,并在其中安插代码,调用其父类的缺省构造函数。

#include <iostream>
using namespace std;

class MBTXPARENT
{
public:
	MBTXPARENT()
	{
		cout << "父类构造函数被调用了" << endl;
	}
};
class MBTX :public MBTXPARENT
{
public:
	int m_i;
	int m_j;

};


int main()
{
	MBTX myb;
	return 1;
}	

运行结果:父类函数被调用了

查看.obj文件:

(3)如果一个类含有虚函数,但没有任何构造函数时

          a.编译器会给我们生成一个基于该类的虚函数表vftable

          b.编译器给我们合成了一个构造函数,并且在其中安插代码: 把类的虚函数表地址赋给类对象的虚函数表指针 (赋值语句/代码);

#include <iostream>
using namespace std;

class MBTX 
{
public:
	int m_i;
	int m_j;


	virtual void MBTXfunc()
	{
		cout << "虚函数" << endl;
	}

};


int main()
{
	MBTX myb;
	return 1;
}	

查看.obj文件:

我们再来看一下我们有自己的构造函数的情况:

#include <iostream>
using namespace std;

class MBTXPARENT
{
public:
	MBTXPARENT()
	{
		cout << "MBTXPARENT()" << endl;
	}
};
class MBTX:public MBTXPARENT
{
public:
	int m_i;
	int m_j;


	virtual void MBTXfunc()
	{
		cout << "虚函数" << endl;
	}

	MBTX()
	{
		m_i = 10;
	}
};


int main()
{
	MBTX myb;
	return 1;
}

结论:当我们有自己的默认构造函数时,编译器会根据需要扩充我们自己写的构造函数代码,比如调用父类构造函数,给对象的虚函数表指针赋值。

(4)如果一个类带有虚基类,编译器也会为它合成一个默认构造函数

虚基类:通过两个直接基类继承同一个简介基类。所以一般是三层结构 ,有爷爷类Grand,有两个父类A,A2,有子类C,大致如下

示例代码:

class Grand //爷爷类
{
public:
};

//两个父类
class A : virtual public Grand
{
public:
};

class A2 : virtual public Grand
{
public:
};



class C :public A, public A2 //这里不需要virtual
{
public:
	C()
	{
		int aa;
		aa = 1;
	}
};


int main()
{
	C cc;
	return 1;
}

我们再来看一下我们有自己的构造函数的情况:

编译器没有合成爷爷类Grand的构造函数

编译器合成了父类A和A2的构造函数

A

A2

编译器合成了子类的构造函数,并向其中添加了调用基类构造函数,虚基类表的代码

猜你喜欢

转载自blog.csdn.net/qq_34805255/article/details/84528515
今日推荐