有两个常见的误解:

1.任何类如果没有定义默认构造函数,就会被合成出一个来。

2.编译器合成的默认构造函数会显式地设定类内每一个数据成员的默认值。

对于第一个误解,并不是任何类在没有显式定义默认构造函数时都会被编译器合成一个默认构造函数。

在以下4种情况下,编译器才会合成默认构造函数,以满足编译器自己的需求(并不是为了满足程序的需求)。

一、父类有默认构造函数(default constructor)

如果一个没有任何构造函数的类派生自 “一个有默认构造函数的” 父类,那么这个派生类的默认构造函数被认为是 ”有用的(被编译器所需求)“,因此需要被合成出来。它会调用父类的默认构造函数。

如果派生类含有多个构造函数,但其中不含默认构造函数,编译器并不会为他合成新的默认构造函数,而是会扩展每一个构造函数,将所有需要调用的默认构造函数的代码安插进去。

二、类中带有类类型成员

如果类A中带有类类型成员,并且这个类类型是有默认构造函数的,那么类A的默认构造函数被认为是 ”有用的“,需要合成。

  1. //X,Y,Z都带有显式默认构造函数
  2. class X
  3. {
  4. public:
  5. X();
  6. };
  7. class Y
  8. {
  9. public:
  10. Y();
  11. };
  12. class Z
  13. {
  14. public:
  15. Z();
  16. };
  17. class A
  18. {
  19. public:
  20. X x; //三个类类型
  21. Y y;
  22. Z z;
  23. A(int a); //带有一个构造函数
  24. private:
  25. int val;
  26. };
  27.  
  28. //程序员对A的构造函数的实现(你所看到的)
  29. A::A(int a)
  30. {
  31. val = a;
  32. }
  33.  
  34. //编译器扩展合成后(编译器认为应该这样)
  35. A::A(int a)
  36. {
  37. //按声明顺序安插代码,调用构造
  38. x.X::X();
  39. y.Y::Y();
  40. z.Z::Z();
  41.  
  42. //显式的用户代码
  43. val = a;
  44. }

三、带有虚函数的类

在编译期间会发生两个扩展行动:

1.编译器会产生一个虚表(存放着类内虚函数的地址)。

2.在每一个类对象中,会有一个额外的虚表指针被编译器合成出来,用来指向相关虚表。

四、带有虚基类的类

虚基类的实现必须满足虚基类在其 ”每一个派生子类的对象中的位置“ 能够于执行期准备妥当。

  1. class A
  2. {
  3. public:
  4. int a;
  5. };
  6. class X :public virtual A
  7. {
  8. public:
  9. int x;
  10. };
  11. class Y :public virtual A
  12. {
  13. public:
  14. int y;
  15. };
  16. class Z :public X, public Y
  17. {
  18. public:
  19. int z;
  20. };
  21.  
  22. //编译时无法确定p->A::a的位置
  23. void fun(X *p){ p->a = ; } //编译时无法确定p的真正类型(基类指针可以指向派生类对象,此时真正类型为派生类类型)
  24.  
  25. int main()
  26. {
  27. fun(new X); //真正类型为X
  28. fun(new Z); //真正类型为Z
  29. return ;
  30. }

编译器无法确定fun()中 ”经由p存取的A::a “的实际偏移位置,因为p的真正类型是可变的,如在main()中既可以是X类型,也可以是Z类型。编译器必须改变存取操作的代码,使A::a延迟到执行时才确定下来。

fun()可以被编译器改写成如下:

  1. //编译器转变操作,其中vcbA是编译器产生的指针,指向虚基类A
  2. void fun(X *p){ p->vcbA->a = ; }
  3.  
  4. //vcbA是在类对象构造期间被完成的

除此四种情况之外,如果类没有声明任何构造函数,他们就会有一个隐式而”无用“的默认构造函数,他们实际上并不会被合成构造出来。

对于第二个误解,在合成的默认构造函数中,只有基类子对象和类类型对象会被初始化,而其他所有的非静态成员(如整数,指针,数组等),都不会初始化,对他们进行初始化的应该是程序员,而非编译器。

注意:值类型的默认值并不是默认构造的初始化。

C++关于编译器合成的默认构造函数的更多相关文章

  1. 合成的默认构造函数定义为delete的一种情况(针对C++11标准)

    1. 默认初始化 如果定义变量时没有指定初值,则变量会被默认初始化,此时变量被赋予了"默认值". 对于类类型的变量来说,初始化都是依靠构造函数来完成的.因此,即使定义某个类的变量( ...

  2. c++ 2.1 编译器何时创建默认构造函数

    我们通常会说当生命一个 class 时,如果我们不为该 class 指定一个 constructor,那么编译器会替我们实现一个 connstructor,那么这种说法一定对吗? 事实上,这是不对的. ...

  3. C++ 合成默认构造函数的真相

    对于C++默认构造函数,我曾经有两点误解: 类如果没有定义任何的构造函数,那么编译器(一定会!)将为类定义一个合成的默认构造函数. 合成默认构造函数会初始化类中所有的数据成员. 第一个误解来自于我学习 ...

  4. C++编译器合成Default Constructor的4种情况

    笔记C++编译器为编译器需要合成Default Constructor的4种情况. 1,Class A内含Class B对象,Class A没有Default Constructor时会在编译时合成D ...

  5. C++对象模型——默认构造函数的合成

    最近在学习C++对象模型,看的书是侯捷老师的<深度探索C++对象模型>,发现自己以前对构造函数存在很多误解,作此笔记记录. 默认构造函数的误解 1.当程序猿定义了默认构造函数,编译器就会直 ...

  6. C++中默认构造函数中数据成员的初始化

    构造函数的任务是初始化数据成员的,在类中,如果没有显示定义任何构造函数,编译器将为我们创建一个构造函数,称为合成的默认构造函数,合成的默认构造函数使用与变量初始化相同的规则来初始化成员.即当类中的数据 ...

  7. 【C++对象模型】构造函数语意学之一 默认构造函数

    默认构造函数,如果程序员没有为类定义构造函数,那么编译器会在[需要的时候]为类合成一个构造函数,而[需要的时候]分为程序员需要的时候和编译器需要的时候,程序员需要的时候应该由程序员来做工作,编译器需要 ...

  8. C++对象模型的那些事儿之三:默认构造函数

    前言 继前两篇总结了C++对象模型及其内存布局后,我们继续来探索一下C++对象的默认构造函数.对于C++的初学者来说,有如下两个误解: 任何class如果没有定义default constructor ...

  9. [C++11] 默认构造函数

    类通过一个特殊的构造函数来控制默认初始化过程.这个函数就是默认构造函数.默认构造函数无需不论什么实參. 我们能够显示的定义默认构造函数也能够让编译器为我们生成默认构造函数. 默认构造函数以例如以下规则 ...

随机推荐

  1. WCF性能优势体现 【转】

    WCF性能优势决定了其受欢迎程度,这些优势主要都体现在:统一性:互操作性:安全与可信赖:兼容性等方面. WCF是使用托管代码建立和运行面向服务(Service Oriented)应用程序的统一框架. ...

  2. Bootstrap+MetroNic_1.5.4 Head meta

    Bootstrap+MetroNic_1.5.4 HTML <meta> 标签设定含义: <meta http-equiv="X-UA-Compatible" c ...

  3. BZOJP1003 [ZJOI2006]物流运输trans

    BZOJP1003 [ZJOI2006]物流运输trans 1003: [ZJOI2006]物流运输trans Time Limit: 10 Sec  Memory Limit: 162 MB Sub ...

  4. Bzoj 2763: [JLOI2011]飞行路线 dijkstra,堆,最短路,分层图

    2763: [JLOI2011]飞行路线 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1728  Solved: 649[Submit][Statu ...

  5. Android应用开发学习之表格视图

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 本文我们来学习一个使用表格视图的程序,下图是该程序的运行效果: 该程序主Activity文件内容如下: packag ...

  6. Code Forces Gym 100971D Laying Cables(单调栈)

    D - Laying Cables Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u ...

  7. pg_dump实例详解(备份postgresql和greenplum数据库)

    一.pg_dump的用法:数据库的导入导出是最常用的功能之一,每种数据库都提供有这方面的工具,例如Oracle的exp/imp,Informix的dbexp/dbimp,MySQL的mysqldump ...

  8. MySql中having字句对组记录进行筛选使用说明

    having字句可以让我们筛选成组后的各种数据 having的用法 having字句可以让我们筛选成组后的各种数据,where字句在聚合前先筛选记录,也就是说作用在group by和having字句前 ...

  9. Zend Framework 2参考Zend\Authentication(数据库表认证)

    + 转载自:Zend Framework 2参考Zend\Authentication(数据库表认证) 介绍 Zend\Authentication\Adapter\DbTable提供对存储在数据库表 ...

  10. jsp+urlrewrite实现html分页简单粗暴实现

    今天去检查一同事写的一分页实现,看了有点郁闷,都说了要换成html实现方便搜索引擎收录,他还是用的js,真是晕.还得帮他解决 好吧.言归正传,我在他的基础上粗暴的修改了下,达到了基本的效果,带数字分页 ...