博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
编译器自动生成默认构造函数的四种情况
阅读量:5944 次
发布时间:2019-06-19

本文共 1721 字,大约阅读时间需要 5 分钟。

在以下四种情况中,如果类中没有定义一个默认构造函数,则编译器会自动生成一个nontrivial的默认构造函数,而不是一个不做事情的trivial默认构造函数:

1.内含一个成员变量,而这个成员变量所属的类中含有默认构造函数,则此时需要为此类生成一个implicit default constructor(隐式的默认构造函数),这个implicit default constructor是nontrivial的,因为内含的成员变量需要进行默认构造操作。

如果内含一个成员变量a,且该成员变量a含有默认构造函数。同时已经对该类定义了默认构造函数但未对a进行操作,则编译器会在自己定义的默认构造函数的开始部分插入一个a所属类的默认构造函数,不然这个自己定义的默认构造函数将会忽视掉a的nontrival默认构造函数。

2.继承自一个类,且该类中有默认构造函数。派生类中没有定义默认构造函数,则编译器会为派生类提供一个上一层基类的默认构造函数。

3.class中声明了一个虚函数。声明了虚函数代表该class中将出现vptr,并需要为虚函数构造一张虚函数表vtbl,这个操作是必须的,因此使该class的默认构造函数成为nontrivial的,因此需要为了这个vptr和vtbl构建默认构造函数,进行初始化操作。

4.带有虚基类的class。因为虚基类的引入,必须要有一个指针或者类似索引的东西来指向虚基类的区域,以使虚基类的派生类们能找到共享的虚基类的存储区域。

如:

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;};

void foo(const A* pa) {pa->i=1024};

main()

{

............

}

其中,因为pa的类型可以改变,它可以是A*,也可以是C*,因此,在编译期并不能确定pa指向的对象到底是什么,而只能等到执行期才能固定pa->i的具体位置(因为pa可能不同,而虚基类一般是存在最底部的,因此pa->i相对于类的起始地址的偏移会随着pa的不同而不同)。因此,为了实现在执行期能确定i的位置,需要在类中在引入一个指针或类似索引的东西,这样,在编译期就可以转化为 pa->_vbcX->i=1024;而_vbcX是由编译期所产生的指针。这样无论pa是A*类还是C*类,他们都是经由_vbcX找到的i,只要在A中与C中都引入_vbcX,这样他们在执行期都能通过同样的方式来确定i的位置。

而正因为需要引入_vbcX这种形式的指针,因此使该类的初始化变得nontrivial,因此需要引入一个nontrivial的默认构造函数,使类在创建时期就构建一个_vbcX指针。

 

C++新手一般有两个常见的误解:

1.任何ckass如果没有定义default constructor,就会被合成一个出来。

实际上,只有上述四种情况中才会生成默认构造函数,因为只有上述四种情况下默认构造函数才是nontrivial的,而诸如类中int a;的初始化,这对编译期来说是trivial的,它的初始化是应该交由程序猿们来实现的。

2.编译期合成出来的default constructor会明确设定"class内每一个data member的默认值"。

实际上,只有nontrivial的成员才需要default constructor对他进行初始化,如类中有默认构造函数的成员变量,vptr.....这种情况。

而trivial的成员的初始化工作不是编译期的职责而是程序设计者的职责。

相反,如果class设计的默认构造函数只对trivial的成员初始化了,编译期则会自己在所定义的默认构造函数中插入对nontrivial的初始化操作(按照声明优先顺序)。

 

转载于:https://www.cnblogs.com/lxy-xf/p/11038207.html

你可能感兴趣的文章
Android新权限机制 AppOps
查看>>
“蓝桥杯”软件大赛入门训练4道题
查看>>
[2010山东ACM省赛] Greatest Number(数的组合+二分搜索)
查看>>
Unable to get the CMake version located at
查看>>
爬虫基本原理
查看>>
Heritage from father
查看>>
css选择器
查看>>
使用多线程
查看>>
Linux-gate.so.1的含义[ZZ]
查看>>
Call指令和Ret指令讲解
查看>>
利用GetPrivateProfileString读取配置文件(.ini)
查看>>
Django--Uploaded Files以及Handlers
查看>>
请求一个action,将图片的二进制字节字符串在视图页面以图片形式输出
查看>>
android 颜色值参考,(有颜色图
查看>>
在IIS(64位)上部署WCF服务访问Oracle数据库
查看>>
UltraISO软碟通U盘安装Centos7 的各种报错及解决方案
查看>>
C# 判断两张图片是否一致,极快速
查看>>
个人在 laravel 开发中使用到的一些技巧(持续更新)
查看>>
Go开发之路 -- 指针类型
查看>>
java 打包的两种方式
查看>>