c++ 2.1 编译器何时创建默认构造函数
我们通常会说当生命一个 class 时,如果我们不为该 class 指定一个 constructor,那么编译器会替我们实现一个 connstructor,那么这种说法一定对吗?
事实上,这是不对的。这个不是我说的,是深入理解C++对象模型说的,原话是:
C++新手一般有两个常见的误解:
- 任何 class 如果没有定义 default constructor,就会被合成出一个来。
- 编译器合成出来的 default constructor 会明确设定 "class 内每一个 data member 的默认值。
下面分别讨论上述四种合成 constructor 的情况:
情况一:一个 class 内含一个 member object,且其 member object 拥有 default constructor
class Foo { public: Foo(), Foo( int ) ... };
class Bar { public: Foo foo; char* str; }; //不是继承,是包含
void foo_bar(){
Bar bar; //Bar::foo 必须在此处初始化
//译注:Bar::foo是一个member object,而其 class Foo 拥有default constructor,符合本小节主题
if( str ) { } ...
}
此时 class Bar 内含一个 member object Foo,并且 Foo 有 default constructor Foo(),那么编译器就会为 Bar 合成一个 default constructor,看起来像这样:
//Bar 的 default constructor 可能会这样合成
//被 member foo 调用 class Foo 的 default constructor
inline
Bar::Bar(){
//C++伪码
foo.Foo::Foo();
}
再一次请你注意,被合成的 default constructor 只满足编译器的需要,而不是程序的需要,为了让这个程序片段能够正确执行,字符指针 str 也需要初始化,那么程序员可能会这么做:
Bar::Bar() { str = ; } //程序员定义的 default constructor
此时编译器还需要初始化 member object foo,但是由于 default constructor 已经被程序员明确定义出来了,编译器没办法合成第二个。编译器采取的行动是:“如果 class A 内含一个或一个以上的 member class object,那么 class A 的每一个 constructor 必须调用每一个 member classes 的 default constructor”,编译器会向用户程序员的 constructors 前面插入必要的 default constructor。插入后可能像这样:
//插入后的 default constructor
//C++伪码
Bar::Bar(){
foo.Foo::Foo(); //插入的 compiler code
str = ; //explicit user code
}
如果有多个 class member objects 都要求初始化操作,将如何做呢?C++会按照 “member objects 在 class 中的声明次序“ 来调用各个 consructors。这一点由编译器完成。并且如果某个 member object 的 default constructor 被程序员定义过了,它照样会被按顺序调用。如:
//程序员对 Snow_White 类所写的 default constructor,显示初始化一个 member object
Snow_White::Snow_White() : sneezy( ){
mumble =
}
它会被扩张为:
Snow_White::Snow_White() : sneezy( ) {
//插入 member class onject
//调用其 constructor
dopey.Dopey::Dopey();
sneezy.Sneezy::Sneezy();
bashfun.Bashfun::Bashful();
//explicit user code
mumble = ;
}
情况二:带有 “default constructor” 的 base class
情况三:带有一个 “virtual Function” 的 class
如果一个 class 声明或继承一个 virtual function,并且缺乏由用户声明的 constructors,编译器会详细记录合成一个 default constructor 的必要信息。如:
class Widget{
public:
virtual void filp() = ;
...
}
void filp( const Widget& widget ) { widget.filp(); }
//假设 Bell 和 Whistle 都派生自 Widget
void foo(){
Bell b;
Whistle w;
filp( b );
filp( w );
}
下面两个扩张步骤在编译期间发生:
- 一个 virtual function table (在 cfront 中被称为 vtbl)会被编译器产生出来,内放 class 的 virtual functions 地址。
- 在每一个 class onject 中,一个额外的 pointer member(也就是 vptr)会被编译器合成出来,内含相关的 class vtbl 的地址。
//widget.filp() 的虚拟引发操作(virtual invocation)的转变
// (* widget.vptr[ 1 ] )( &widget )
- 1 表示 filp() 在 virtual table 中的固定索引
- &widget 代表要交给 “被调用的某个 filp() 函数实体” 的 this 指针
情况四:带有一个 ”virtual base class“ 的 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, pulic B { public: int k; };
//无法在编译使其决定(resolve)出pa->X::i 的位置
void foo( const A* pa) { pa->i = ; }
main()
{
foo( new A );
foo( new C );
// ...
}
//可能的编译器转变操做,中间加了一层
void foo( const A* pa ) { pa->__vbcX->i = ; }
- 编译器合成出来的 default constructor 会明确设定 "class 内每一个 data member 的默认值
c++ 2.1 编译器何时创建默认构造函数的更多相关文章
- C++关于编译器合成的默认构造函数
有两个常见的误解: 1.任何类如果没有定义默认构造函数,就会被合成出一个来. 2.编译器合成的默认构造函数会显式地设定类内每一个数据成员的默认值. 对于第一个误解,并不是任何类在没有显式定义默认构造函 ...
- C++编译器会对没有构造函数的类生成默认构造函数吗?(有必要的时候才生成,要看情况。有反汇编验证)
之前在上C++的课的时候,印象中有那么一句话:如果一个类没有任何构造函数,那么编译器会生成一个默认的构造函数 今天在看<深度探索C++对象模型>的第二章:“构造函数语意学”的时候发现之前听 ...
- C++ 合成默认构造函数的真相
对于C++默认构造函数,我曾经有两点误解: 类如果没有定义任何的构造函数,那么编译器(一定会!)将为类定义一个合成的默认构造函数. 合成默认构造函数会初始化类中所有的数据成员. 第一个误解来自于我学习 ...
- C++默认构造函数的问题
C++ defaul construct :缺省构造函数(默认构造函数) 定义:第一种 构造函数没有参数,即是 A()形式的 第二种 构造函数的全部参数由缺省值提供,A(int a=0,int ...
- C++默认构造函数的一点说明
大多数C++书籍都说在我们没有自己定义构造函数的时候,编译器会自动生成默认构造函数.其实这句话我一直也是 深信不疑.但是最近看了一些资料让我有了一点新的认识. 其实我觉得大多数C++书籍之所以这样描述 ...
- C++对象模型——默认构造函数的合成
最近在学习C++对象模型,看的书是侯捷老师的<深度探索C++对象模型>,发现自己以前对构造函数存在很多误解,作此笔记记录. 默认构造函数的误解 1.当程序猿定义了默认构造函数,编译器就会直 ...
- C/C++ 关于默认构造函数
前言: 在C++中,对于一个类,C++的编译器都会为这个类提供四个默认函数,分别是: A() //默认构造函数 ~A() //默认析构函数 A(const A&) //默认拷贝构造函数 A&a ...
- C++ //构造函数调用规则 //1.创建一个类,C++编译器会给每个类添加至少3个函数 //默认构造(空实现) //析构函数(空实现) //拷贝函数(值拷贝) //2.如果我们写了有参构造函数 编译器就不会提供默认构造函数 但是会提供拷贝构造函数 //3.如果我们写了拷贝函数 编译器就不再提供 默认 有参 构造函数
//构造函数调用规则 #include <iostream> using namespace std; //1.创建一个类,C++编译器会给每个类添加至少3个函数 //默认构造(空实现) ...
- C++编译器何时为用户提供默认构造函数
第一种是类成员中有成员是类对象,并且该成员的类含有默认构造函数,那么C++编译器会帮你给这个类也生成一个默认构造函数,用来调用其成员对象的构造函数,完成该成员的初始化构造.需要强调的是,如果这个成员的 ...
随机推荐
- 用keytool制作证书并在tomcat配置https服务(三)
用keytool制作证书并在tomcat配置https服务(一) 用keytool制作证书并在tomcat配置https服务(二) 用keytool制作证书并在tomcat配置https服务(四) 模 ...
- Shell编程学习之重定向
这一篇讲一下重定向 有些时候你想要保存某些命令产生的输出而不是在显示器上显示它. 为了应对这样的问题 bash shell 也就提供了一些重定向的操作符. 我们先了解一些基本的应用. 输出重定向 输出 ...
- VisualSVN 4.0.10 破解版 附上破解过程
VisualSVN一般情况下使用不需要破解,可以直接使用社区授权.但是社区授权不支持域用户. 如果要再域下面使用就需要破解了. 原版的VisualSVN和破解后的DLL已打包上传(仅供学习使用) 破解 ...
- 模块和控制器包含在JS文件中
在AngularJS应用程序中,通常是把模块和控制器包含在javascript文件中. <!DOCTYPE html><html><head><meta ht ...
- C# 使用布尔操作符
布尔操作符(Boolean operator)是求值结果要么为true,要么为false的一种操作符.C#提供了几个非常有用的布尔操作符,其中最简单的是NOT(求反)操作符,它使用感叹号(!)来表示. ...
- shell编程中的vim命令说明
vim命令模式: 1.一般命令模式 2.编辑模式 3.底行命令行命令模式 一般命令模式 直接用字符操作编辑模式 可以写文档(跟txt有点像)底行命令模式 先按'ESC',在按下“:”,之后在输出命令 ...
- ant Design表单验证笔记
1.pattern正则验证 <Col md={12} sm={24}> <FormItem {...formItemLayout} label="班数"> ...
- nginx+php-fpm结构模型剖析及优化(转载)
一.nginx和php-fpm的关系和分工 nginx是web服务器,php-fpm是一个PHPFastCGI进程管理器,两者遵循fastcgi的协议进行通信,nginx负责静态类似html文件的处理 ...
- Linux实战教学笔记16:磁盘原理
第十五节 磁盘原理 标签(空格分隔): Linux实战教学笔记 1,知识扩展 非脚本方式的一条命令搞定批量创建用户并设置随机10位字母数字组合密码. 1.1 sed的高级用法 [root@chensi ...
- OSI七层模型 学习摘要
OSI参考模型是计算机网路体系结构发展的产物.它的基本内容是开放系统通信功能的分层结构.这个模型把开放系统的通信功能划分为七个层次,从邻接物理媒体的层次开始,分别赋于1,2,……7层的顺序编号,相应地 ...