rangf的专栏

C++ primer中的三个地方讲解了默认构造函数:

P44变量初始化规则

P227函数(构造函数)

P388类(构造函数初始化式)

P392默认构造函数

一, 变量初始化规则(P44和P227)

1,对于类类型的成员,调用该成员所属类自身的默认构造函数实现初始化。

2,,内置类型成员的初值依赖于对象定义的位置,如果对象在全局作用域中定义(既不在任何函数中)或定义为静态局部对象,则这些成员将被初始化为0;

3,如果对象在局部作用域中定义,则这些成员没有初始化!

二,初始化列表

由P49和P50可知:const和引用类型的成员,不能对它们赋值,所以必须在定义时初始化!

所以,对于const对象或引用类型的对象,在开始执行构造函数的函数体之前,要完成初始化!(P389)

初始化const或引用类型数据成员的唯一机会是在构造函数初始化列表中!

同时,由于没有默认构造函数的类类型的成员,编译器尝试使用默认构造函数将会失败,所以也必须在初始化列表中初始化!

三, 默认构造函数的定义

在P225定义了默认构造函数:没有形参。

再看看百度百科的定义:默认构造函数(default constructor)就是在没有显式提供初始化式时调用的构造函数。它由不带参数的构造函数,或者为所有的形参提供默认实参的构造函数定义。如果定义某个类的变量(对象)时没有提供初始化式就会使用默认构造函数。

在P227中说:如果没有为一个类显示定义任何构造函数,编译器将自动为这个类生成默认构造函数,通常称为合成的默认构造函数,它一般用于仅包含类类型成员的类!它不会自动初始化内置类型或复合类型的成员。所以,对于含有内置类型或复合类型成员的类,通常应该定义他们自己的默认构造函数初始化这些成员。

问题:是不是所有的类都有默认构造函数?答案是否定的。

1, 虽然编译器会为类自动生成一个合成的默认构造函数,但是它仅对于包含类类型成员的类。所以对于只含有内置类型或复合类型成员的类,如果不自定义他们的构造函数,编译器不会自动为其生成一个合成的默认构造函数的!

class Sales_item{public:……//Sales_item():units_sold(0), revenue(0.0){}private://std::string isbn;//去掉它,编译器就不会为本类自动生成默认构造函数!unsigned units_sold;double revenue;};

2, 当然,如果类显示定义了默认构造函数,但是没有为其类类型的成员在初始化列表中显示初始化,那么会调用该成员所属类自身的默认构造函数实现初始化!这部分工作也是由编译器自动添加到当前默认函数完成的!

class Sales_item{public:……Sales_item():units_sold(0), revenue(0.0){}//如果为isbn成员也在这里显示初始化,那么编译器将忽略string的默认构造函数~private:std::string isbn;//虽然显示定义的默认构造函数,但是它的初始化是由编译器自动添加完成的!unsigned units_sold;double revenue;};</span>

3, 如果显示定义带参数的构造函数(任意的构造函数),那么编译器不会再生成默认构造函数!

(通常也会自动生成一个合成的默认构造函数,此时,类有两个构造函数!同时编译器也完成上面第2点的工作。这句话是×的,见P392)

class Sales_item{public:……Sales_item(double rev):units_sold(0), revenue(rev){}private:std::string isbn;unsigned units_sold;double revenue;};

关于这一点需要上机测试(TODO:测试结果之后附上),因为百科上解释如下:

一个类显式地声明了任何构造函数,编译器不生成公有的默认构造函数。这这种情况下,如果程序需要一个默认构造函数,需要由类的设计者提供。

4,如果派生列的基类中有自定义的nontrivial default constructor,那么编辑器会为每一个派生类合成一个nontrivial default constructor,以调用基类自定义的nontrivial default constructor。

5,如果一个类里隐式的含有Virtual tabel(Vtbl)或者pointer member(vptr)

6,如果一个类虚继承与其他类

编译器会为每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有对象共享。类的每个虚成员占据虚函数表中的一行。如果类中有N个虚函数,那么其虚函数表将有N*4字节的大小。 虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。 编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。 这意味着可以通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。

vtbl和vptr参考:

如对本文中的结论有异议,请留言联系,不胜感激!

可是我要如何在浅薄的纸上为你画上我所有的命轮?

rangf的专栏

相关文章:

你感兴趣的文章:

标签云: