C++默认构造函数

来源:互联网 发布:jackson string json 编辑:程序博客网 时间:2024/06/11 01:56

之前一直以为C++的默认构造函数,只要我们没有主动添加,编辑器都会默认合成。原来不是这样子的。而只在编译器需要的时候产生出来。具体是在什么时候需要?产生出来做什么事情?下面来看几个例子。

exp1:

class Foo { public: int a; int b};void main(){Foo foo;if(foo.a == 0)//do something...}

这个例子中如果程序需要foo.a初始化为0,则需要程序员去实现。因为这是这是出于程序的需要,不是编辑器所需要的,所以需要人为去添加构造函数。

下面来看下编译器合成默认构造函数的几种情况:

1、“带有 Default Constructor” 的Member Class Object (成员类对象带有默认构造函数)

exp2:

class Foo { public: Foo();int a; int b};class Bar { public: Foo foo; char ch;};void main(){Bar bar;//Bar::foo必须在此处初始化...}
在这个例子中编译器会合成一个默认构造函数,该构造函数用来调用类Foo的默认构造函数来处理Bar::foo。

但这里并不对Bar::ch进行初始化,这是程序员要做的事情。
再考虑另一种情况,如果我们自己提供了默认构造函数:

//程序员定义的默认构造函数Bar::Bar() { ch = '0';}
但是编译器还是要对Bar::foo初始化,显然不能再加一个默认构造函数。因此编译器就会在已有的默认构造函数插入部分代码。

可能会是这个样子:

//扩张后的默认构造函数//C++伪代码Bar::Bar() {foo.Foo::Foo();//编译器添加的代码ch = '0';}
2、“带有 Default Constructor”的Base Class

exp3:

class A{public:A(){};};class B:public A{};void main(){B b;...}
类B派生自类A,当B 实例化对象b时,编译器会合成一个类B的默认构造函数,通过该构造函数调用基类A的默认构造函数。

如果子类B中有多个构造函数,但都没有默认构造函数。则编译器会扩展现有的每个构造函数,加入相应的代码来调用基类A的默认构造函数。
3、“带有一个 Virtual Function”的Class

1.类声明(或继承)一个virtual function

2.类派生自一个或多个virtual base classes;
exp4:

class A{public:virtual void fun() = 0;};class B:public A{};void main(){B b;...}
这里编译器同样会合成一个默认构造函数,这里的默认构造函数主要做:

1.创建虚函数表(vtbl),保存基类A的虚函数地址

2.保存虚函数表指针(vptr),指向虚函数表
4、“带有一个 Virtual Base Class”的Class

exp5:

class B:public virtual A{ public:int j; };class C:public virtual A{ public:int d; };void foo( const B* pb) { pb->i = 1; }void main(){foo( new B);...}
在这个例子中编译器无法确定有pb而存取的X::i的实际偏移地址,因为pb的类型可变。因此编译器在子类B中安插一个指针_vbcA来存取基类A
在编译器中可能的代码样子:

void  foo ( const B* pb) { pb->_vbcA->i = 1; }
其中_vbcA指向虚基类A。如果子类B没有构造函数,这部分的工作也需要编译器合成一个默认构造函数。


总结

编译器合成默认构造函数的主要目的有:

1.调用基类的默认构造函数;

2.初始化虚函数机制或虚基类机制;

在合成的默认构造函数中只有 base class subobject 和member class objects会被初始化,其他的数据成员变量,如整数,数组等等都不会被初始化。

---学习《深入探索C++对象模型》笔记

0 0
原创粉丝点击