/*
* 分析obj(目标文件),构造函数语义
构造函数:
1.默认构造函数(又叫缺省构造函数): 没有参数的构造函数
2.传统知识认为,若未定义任何构造函数,编译器会隐式自动定义一个默认构造函数,叫"合成的默认构造函数"
==>
结论: 只有在必要的时候,编译器才会自动定义一个"合成的默认构造函数"; 而不是必然或者必须才合成出来.
(重点为必要的时候是什么时候)
前提知识:
每个.cpp源文件经过编译器编译都会生成一个.obj(linux下.o)文件(目标文件,同时也是二进制文件), 最终把很多此类文件链接在一起,就会生成一个可执行文件.
步骤:(windows下)
1.生成obj
2.用dumpbin把.obj文件导出成可查看文件my.txt(COFF,即通用对象文件格式,Common Object File Format)
3.在my.txt中寻找MBTX::MBTX ==> 未找到 ==> 未合成出合成的默认构造函数
====>编译器在哪些时候会把默认的构造函数合成出来?
1.该类MBTX未有任何构造函数,担包含一个类类型的成员m,同时该成员所属于的类MATX有一个缺省构造函数:
此时该类MBTX会合成一个默认的构造函数,在合成的默认构造函数中安插代码,为了调用类类型成员所属的类MATX的缺省构造函数MATX::MATX().
注意,类MBTX中可含有若干个类类型的成员,而该类的合成的默认构造函数
*/
#include <iostream>
using namespace std;
class MATX
{
public:
MATX()//缺省构造函数
{
printf("I am MATX\n", this);
}
};
class MBTX
{
public:
int c;
int d;
MATX m;//类类型成员变量
void funB()
{
printf("Hello\n", this);
}
};
int main(int, char**)
{
MBTX b;
//输出:
// I am MATX
return 1;
}
/*
* 分析obj(目标文件),构造函数语义
// ====>编译器在哪些时候会把默认的构造函数合成出来?
// 1.该类MBTX未有任何构造函数,担包含一个类类型的成员m,同时该成员所属于的类MATX有一个缺省构造函数:
// 此时该类MBTX会合成一个默认的构造函数,在合成的默认构造函数中安插代码,为了调用类类型成员所属的类MATX的缺省构造函数MATX::MATX().
// 注意,类MBTX中可含有若干个类类型的成员,而该类的合成的默认构造函数
2.父类带缺省构造函数,子类没有任何构造函数.因为父类的缺省构造函数要被调用,所以编译器会为这个子类合成出一个默认构造函数
合成的目的是为了调用父类的构造函数.换句话说,编译器合成了默认的构造函数,并在其中安插代码,调用其父类的缺省构造函数
*/
#include <iostream>
using namespace std;
class MATX
{
public:
MATX()//缺省构造函数
{
printf("I am MATX\n", this);
}
};
class MBTXPARENT
{
public:
MBTXPARENT()
{
printf("Hello,i am MBTXPARENT\n");
}
};
class MBTX: public MBTXPARENT
{
public:
int c;
int d;
// MATX m;//类类型成员变量
void funB()
{
printf("Hello\n", this);
}
};
int main(int, char**)
{
MBTX b;
//输出:
// Hello,i am MBTXPARENT
return 1;
}
/*
* 分析obj(目标文件),构造函数语义
// ====>编译器在哪些时候会把默认的构造函数合成出来?
// 1.该类MBTX未有任何构造函数,担包含一个类类型的成员m,同时该成员所属于的类MATX有一个缺省构造函数:
// 此时该类MBTX会合成一个默认的构造函数,在合成的默认构造函数中安插代码,为了调用类类型成员所属的类MATX的缺省构造函数MATX::MATX().
// 注意,类MBTX中可含有若干个类类型的成员,而该类的合成的默认构造函数
// 2.父类带缺省构造函数,子类没有任何构造函数.因为父类的缺省构造函数要被调用,所以编译器会为这个子类合成出一个默认构造函数
// 合成的目的是为了调用父类的构造函数.换句话说,编译器合成了默认的构造函数,并在其中安插代码,调用其父类的缺省构造函数
3.如果一个类含有虚函数,但该类没有任何构造函数时
当存在虚函数时:
(1)编译器会自动生成一个基于该类的虚函数表vftable(表中的每一项都记录着此类中每一个虚函数的首地址,虚函数表是跟着类走的)
(2)编译器合成了一个构造函数,并在其中安插代码:目的是把虚函数表地址赋给类对象的虚函数表指针(相当于编译器在合成的缺省构造函数中加了一句赋值语句).
(而虚函数表指针可以看作表面上看不见的一个类的成员函数)
==>当有虚函数存在,且该类生成一个对象时,这个类所属的对象会自带一个看不见的虚函数表指针,此指针就指向与该类有关的虚函数表
(而之所以这么麻烦,是因为虚函数的调用存在一个多态问题,所以需要用到虚函数表指针这个概念)
注意: 若在该类中加上一个缺省构造函数,而此时编译不会自动生成一个合成的默认构造函数,但是会在该类中本来有的构造函数中加上一些东西:
在生成对象时:
假设该类继承了基类,则会在其中安插代码:目的是调用基类的构造函数;
假设该类中有虚函数时,也会在其中安插代码:目的是生成虚函数表,以及把虚函数表地址赋给类对象的虚函数表指针;
==>当有自己的构造函数时,编译器会根据需要扩充构造函数
==>编译器干了很多事,有构造函数时,根据需要扩充构造函数,无构造函数时,需要情况下合成构造构造函数
*/
#include <iostream>
using namespace std;
class MATX
{
public:
MATX()//缺省构造函数
{
printf("I am MATX\n", this);
}
};
class MBTX
{
public:
int c;
int d;
void funB()
{
printf("Hello,i am funB\n", this);
}
virtual void mvirtualfunc()
{
printf("Hello,i am mvirtualfunc\n", this);
}
};
int main(int, char**)
{
MBTX b;
//输出:
// 无任何输出,但是保存my.txtx后可以搜索到MBTX合成的默认构造函数
return 1;
}
/*
* 分析obj(目标文件),构造函数语义
// ====>编译器在哪些时候会把默认的构造函数合成出来?
// 1.该类MBTX未有任何构造函数,担包含一个类类型的成员m,同时该成员所属于的类MATX有一个缺省构造函数:
// 此时该类MBTX会合成一个默认的构造函数,在合成的默认构造函数中安插代码,为了调用类类型成员所属的类MATX的缺省构造函数MATX::MATX().
// 注意,类MBTX中可含有若干个类类型的成员,而该类的合成的默认构造函数
// 2.父类带缺省构造函数,子类没有任何构造函数.因为父类的缺省构造函数要被调用,所以编译器会为这个子类合成出一个默认构造函数
// 合成的目的是为了调用父类的构造函数.换句话说,编译器合成了默认的构造函数,并在其中安插代码,调用其父类的缺省构造函数
// 3.如果一个类含有虚函数,但该类没有任何构造函数时
// 当存在虚函数时:
// (1)编译器会自动生成一个基于该类的虚函数表vftable(表中的每一项都记录着此类中每一个虚函数的首地址,虚函数表是跟着类走的)
// (2)编译器合成了一个构造函数,并在其中安插代码:目的是把虚函数表地址赋给类对象的虚函数表指针(相当于编译器在合成的缺省构造函数中加了一句赋值语句).
// (而虚函数表指针可以看作表面上看不见的一个类的成员函数)
// ==>当有虚函数存在,且该类生成一个对象时,这个类所属的对象会自带一个看不见的虚函数表指针,此指针就指向与该类有关的虚函数表
// (而之所以这么麻烦,是因为虚函数的调用存在一个多态问题,所以需要用到虚函数表指针这个概念)
// 注意: 若在该类中加上一个缺省构造函数,而此时编译不会自动生成一个合成的默认构造函数,但是会在该类中本来有的构造函数中加上一些东西:
// 在生成对象时:
// 假设该类继承了基类,则会在其中安插代码:目的是调用基类的构造函数;
// 假设该类中有虚函数时,也会在其中安插代码:目的是生成虚函数表,以及把虚函数表地址赋给类对象的虚函数表指针;
// ==>当有自己的构造函数时,编译器会根据需要扩充构造函数
// ==>编译器干了很多事,有构造函数时,根据需要扩充构造函数,无构造函数时,需要情况下合成构造构造函数
4.如果一个类带有虚基类,编译器也会为他合成一个默认构造函数.
虚基类:通过两个直接基类继承同一个间接基类.(三层结构:爷爷Grand,两个爹A,A2,一个孙子C)
对于虚基类结构,编译器为子类和父类都产生了合成默认构造函数
*/
#include <iostream>
using namespace std;
class Grand//爷爷类
{
public:
};
class A : virtual public Grand//爹类
{
public:
};
class A2 : virtual public Grand//爹类
{
public:
};
class C : public A,public A2//孙子类
{
public:
};
int main(int, char**)
{
C cc;
//输出:
// 无任何输出,但是保存my.txtx后可以搜索到A,A2,C合成的默认构造函数:
// 产生了vbtable(虚基类表)
return 1;
}