C++对象模型之Default Constructor的建构操作

来源:互联网 发布:xbox手柄驱动mac 编辑:程序博客网 时间:2024/06/05 02:03

序言:

为了满足编译器的需要, 当类设计者没有显示的声明Default Constructor的时候, 编译器为满足编译程序的需要, 将会按照一定的规则自动生成Default Constructor。

一、编译器自动合成Default Constructor的四种情况

1、类的成员对象包含默认构造函数

2、继承的基类有默认构造函数

3、该类有虚函数, 需要初始化virtual function机制

4、该类继承自virtual base class, 需要配置virtual base class subobject机制

☆ 注意事项:若类中有其他构造函数, 编译器将不会生成default Constructor


1、类的成员对象包含默认构造函数 

 如果一个class没有任何constructor,但它内含一个member object,而那个member object的类有default constructor,那么这个class的implicit default constructor 就是“nontrivial”,编译器需要为此class 合成出一个default constructor,不过这个合成操作只有在constructor真正需要被调用时才会发生。 

如果有多个类成员对象都要求用默认构造函数进行初始化操作? 那么在C++语言中将会按照“成员对象在class中的生命次序”来调用constructor, 但是编译器自动插入这些代码将会插入在explicit user code之前。

class Month{ public:Month(){ cout <<" Month Construtor\n";}};class Eye{ public:Eye(){ cout <<" Eye Construtor\n";}};class Ear{ public:Ear(){ cout <<" Ear Construtor\n";}};class Head{private:Month month;Eye eye;Ear ear;int mumble;public:Head(){cout <<"美美哒";mumble = 2;}};
而Head的默认构造函数在经过编译器扩张后的C++伪代码如下

Head::Head(){// 编译器自动拓展代码 month.Month::Month();eye.Eye::Eye();ear.Ear::Ear();// explicit user codecout <<"美美哒";mumble = 2;}

对以上的运行结果:



2、继承的基类有默认构造函数 

如果类设计者把Derived设计成了没有构造函数的代码,但是他的Base类(基类)有默认构造函数, 那么编译器将会自动为其按照基类的顺序生成构造函数


3、该类有虚函数, 需要初始化virtual function机制 

(1)、当class声明(或继承)一个virtual function的时候

(2)、class 派生自一个继承串链, 其中有一个或更多的virtual base classes

当出现以上情况的时候编译器将会自动合成默认构造函数, 去构造虚函数表的内容。

class Animal{public:virtual void run() = 0;};class Cat: public Animal{ public: void run(){cout << "Cat run\n";}};class Pig: public Animal{ public: void run(){cout << "Pig run\n";}};void run(Animal& a){a.run();}void allrun(){Cat c;Pig p;run(c);run(p);}
(1)一个virtual function table(在cfront中称为vtbl)会被编译器产生出来, 内存class的virtual functions地址

(2) 在每个class object中, 一个额外的pointer member(也就vfptr)会被编译器合成出来, 内含相关的virtual function table地址。


在allrun当中, Animal.run()的虚拟引发操作会被重新改写, 以使Animal的vptr和vtbl中的run条目

// Animal.run()的虚拟引发操作的转变( *Animal.vptr[1] ) (&Animal)
☆ 1、代表run在virtual table中的固定索引

☆ &Animal代表需要交给“被调用的某个run()函数实体”的this指针

4、该类继承自virtual base class, 需要配置virtual base class subobject机制

Virtual base clas的实现法在不同的编译器之间有极大的差异。然而,每一种实现法的共通点在于必须使virtual base class 在其每一个derived class object中的位置,能够于执行期准备妥当。例如下面这段程序代码中:

class X{public:int i;};class A:public virtual X{public:int j;};class B:public virtual X {public:double d;};class C:public A,public B {public:int k;};//无法在编译时期决定(resolve)t出pa->x::i的位置void foo(const A *pa){pa->i = 1024;} int main() {foo(new A);foo(new C);// ....return 0;}
编译器无法出定住foo() 之中“经由pa而存取的X::x”的实际偏移位置,因为pa的真正类型可以改变。编译器必须改变“执行存取操作”的那些码,使X::i可以延迟至执行期才决定下来.原先cfront的做法是靠“在derived class object的每一个virtual base classes中安插一个指针”完成。所有“经由reference或pointer 来存取一个virtual base class”的操作都可以通过相关指针完成.在我的例子中,foo() 可以被改写如下,以符合这样的实现策略:
void foo(const A* pa){pa->__vbcx->i = 1024;}
其中__vbcx表示编译器所产生的指针, 指向virtual base class X

二、总结

1、合成构造函数只会初始化member class object和base class subobjects, 所有其他nonstatic data member(整数, 指针, 字符, 浮点等)都不会被初始化

2、任何class 如果定义default constructor, 都会被合成出来---------这是错误的说法

3、编译器合成出来的默认构造函数会明确初始化每一个data member为默认值  -----这是错误的说法

阅读全文
0 0
原创粉丝点击