深度探索C++对象模型笔记(二)

本文详细解析了C++中DefaultConstructor、CopyConstructor等构造函数的工作原理及应用场景,探讨了DefaultMemberwiseInitialization、BitwiseCopy语义等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 
Default Constructor 构建
ARM
中指出:Default Constructor在需要的时候被编译产生出来。这里要注意的是在需要的时候这个关键,这种需要分为程序的需要和编译器的需要,如果是程序的需要,那么Default Constructor是程序员的责任。在C++标准中指出nontrivial default constructor就是在ARM中指出的编译器需要的那种default constructor
四种nontrivial default constructor
带有default Constructor的Member Class  Object
如果一个类,如CA,没有default constructor,但他有一个对象类型的成员变量,那么这个类的隐含默认构造函数是一个nontrivial default constructor,编译器会自动为该类生成构造函数,构造函数生成的时机是在这个构造函数真正被调用的时候,生成的是inline类型的代码,这样可以防止一个程序中多处调用类CA的构造函数而生成多个构造函数。如果类CA已经定义了默认的构造函数,那么编译器就不会合成一个构造函数,而仅仅在程序员已经定义好的默认构造函数中插入需要合成的代码,即各个成员对象的默认构造函数,确保在调用程序员定义的代码之前先调用编译器生成的代码。如果CA中含有多个对象类型的变量,那么编译器按照这些对象在类中的申明顺序插入各个对象的默认构造函数,然后才是程序员的代码。
带有default constructor 的基类
如果一个类CD没有任何constructor,而这个类从带有默认构造函数的基类CB继承,那么我们认为CDdefault constructor nontrivial的,因此他的default constructor 需要被合成出来,合成以后的default constructor 它将按照申明的顺序调用CBdefault constructor 。对CD来说这个合成出的构造函数同显式定义的构造函数没有区别。
如果CD提供了构造函数,但唯独没有default constructor ,那么编译器会在现有的每个构造函数中插入CB的默认构造函数,而不会重新合成default constructor 。如果同时存在带有default constructor 的对象成员存在,这些对象成员的default constructor 会在所有基类的default constructor 调用之后被调用。
带有一个Virtual Function的类
无论类继承得到或者是类内部本身申明了Virtual Function,编译器在编译过程中记录两个内容:virtual function tablevtbl),存放类的virtual function地址;在每个类对象中,额外的指针vptr被产生,指向vtbl。编译器对于这样的类的每个构造函数会插入相应的代码取保在每个对象内部插入vptr,对于未定义default constructor 的类,编译器会合成一个构造函数来完成这些工作。
带有virtual基类的类
基类对象元素的在子类的位置必须确保在执行期必须准备好,即运行时必须确定。各种编译器的实现方法不同,但目标是一致的。
总结:以上四种情况,编译器会合成构造函数,除这些情况之外,对于那些没有申明任何类型构造函数的类,我们仅仅能说他有implicit trivial default constructor,实际上并不是被合成出来的。
Copy Constructor的构建
有三种情况下会调用默认构造函数:
1.
显式调用: class X { ... };   X x;   X xx = x; 在这里xx显式调用了拷贝构造函数
2.
对象作为形式参数传递到函数,该参数未申明为引用或者指针
3.
对象作为函数的返回值返回
Default Memberwise Initialization
如果一个类没有提供构造函数,则如果用一个对象初始化另一个同类的对象是通过Default Memberwise Initialization来实现的。Default Memberwise Initialization拷贝每个内建的或者从基类继承来的数据成员。对于类对象型的成员变量,不会直接拷贝,而是递归地调用该类的Memberwise Initialization
 
Bitwise Copy语义
有四种情况下不会存在Bitwise Copy语义,即nontrivial的,如果没有定义default copy function,这时编译器需要合成出一个copy function
    1
A的成员变量中包含一个对象类型(类B)的对象,类B包含一个显式定义的拷贝构造函数,则称类A不具有Bitwise Copy语义:没有搞明白为什么
    2.
A的基类类B包含一个显式定义的拷贝构造函数,则称类A不具有Bitwise Copy语义:notrivial default constructor 语义决定的
    3.
A中定义了一个虚函数,或者从基类继承了一个虚函数,则称类A不具有Bitwise Copy语义:有vptr存在,Bitwise Copy会将vptr替换
    4.
A的祖先是Virtual继承的,则称类A不具有Bitwise Copy语义:每个从virtual基类得到的子类,必须能准确定位到基类的位置,其他的没有明白
程序转化语义
显式的初始化
void foo_bar() {
   X x1( x0 );
   X x2 = x0;
   X x3 = x( x0 );
   // ...
}
初始化为
void foo_bar() {
   X x1;
   X x2;
   X x3;
   x1.X::X( x0 );
   x2.X::X( x0 );
   x3.X::X( x0 );
   // ...
}
参数的初始化
X xx = arg;
void foo( X x0 );
如果调用foo( xx )则程序转化为
X __tmp0;
__tmp0.X::X( xx );
foo( __tmp0 );
同时函数foo声明为void foo( X& x0 ); 这样只需要构建一次X的实例
返回值的初始化
X bar()
{
   X xx;
   // process xx ...
   return xx;
}
转换为
void
bar( X& __result )
{
   X xx;
   // compiler generated invocation
   // of default constructor
   xx.X::X();
   // ... process xx
   // compiler generated invocation
   // of copy constructor
   __result.X::X( xx );
   return;
}
用户层面做优化
定义一个计算用的构造函数
Named Return Value,将返回值定义为引用型的参数
初始化列表
对于以下几种情况,必须使用初始化列表才能通过编译
1.
初始化一个引用类型的成员变量
2.
初始化一个const类此能够的成员变量
3.
调用基类的构造函数,而基类的构造函数有一系列参数(书上的那个例子个人觉得很经典,值得学习,以往只知道这么使,但不知道为什么这样使)
4.
调用成员类对象的构造函数,而成员类的构造函数有一系列参数
注意
1.
初始化列表的初始化顺序是按照在类的声明中出现的顺序初始化的,并不是按照初始化列表中的顺序初始化。
2.
在类的构造函数中调用类的成员函数是允许的,因为这个类对象的this指针已经被完全构建
 
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值