构造函数语意学和Data语意学

此篇为<<deep in C++ object model>>读书笔记。

构造函数语意学

默认构造函数

对于classX,如果没有任何user-declared constructor,那么会有一个default-constructor被暗中声明,一个被暗中声明出来的作出的行为是无法定义的。
本世纪两个最大的两个误解
1.任何class如果没有定义default constructor都会被合成一个出来。
2.编译器合成出来的default constructor会明确设定每一个datamember的默认值。


必须由编译器合成构造函数的四种情况
带有Default constructor的member class object
如果一个class没有任何constructor,但它包含一个memeber object,而后者有default constructor,编译器需要为此class合成一个default constructor。如果classA内含一个或一个以上的member classed  的default constructor编译器会扩张已存在的constructors,在其中安插一些代码,使得user code在被执行之前,先调用必要的default constructors。


如果有多个class memeber objects都要求constructor初始化操作,要求以member objects在class中的声明次序来调用每个constructors为每一个constructor安插代码,以member声明次序调用每一个member所关联的default constructors。


带有default constructor的Base Class
      派生类中没有任何构造函数,则合成的构造函数会调用上一层的base classes的default constructors。如果设计者提供多个constructors,但其中没有default constructor,编译器会扩张现有的每个constructors,将有必要的default constructors代码加进去。


带有virtual function的class
有virtual function的情况:
1.class声明或继承一个cirtual function;
2.class派生自一个继承链,其中有一个或多个virtual base class;
两个扩张操作会在编译期间发生:
1.一个virtual function table会被编译器产生出来,里面放置class的virtual functions的地址;
2.在么个class object中,一个额外的pointer member(指向虚函数表的指针)会被编译器合成出来,指向虚函数表。


编译器必须为每一个基类和派生类的object的vptr设定初值,放置释放的虚函数表地址,对于class所定义的每一个constructor,编译器会安插一些代码来做这样的事情。没有声明任何构造函数的class,会合成default constructor。


带有virtual base class 的类
需要在执行期才能决定一些行为,需要在derived class object的每一个virtual bass class 中安插一个指针。
对于class所定义的每一个constructor,编译器会安插那些允许每一个virtual base class 的执行器存取操作的代码。


类成员的初始化
必须使用成员初始化列表的情况:
1.当初始化一个reference member时;
2.当初始化一个constmember时;
3.当调用一个base class的带参constructor时;
4.当拥有一个member class的带参constructor。


初始化列表的编译器行为 - 编译器会一一操作initialization list,以适当的次序在constructor之内安插初始化操作,并且在任何explicit user code 之前。微妙之处在于list中项目次序是由class中的members声明次序决定,不是由初始化列表中的排列次序决定。


 继承体系下的对象构造

编译器会扩充每一个constructor,扩充程度视class T的继承体系而定。

1. 记录在memeber initialization list 中的 data members 初始化操作会被改进constructor 的函数本身,并以members的声明顺序为顺序;

2. 如果有一个member并没有出现在member initialization list之中,但它有一个default constructor,那么该default constructor必须被调用;

3.在那之前,如果class object有virtual table pointers,它们必须被设定初值,指向适当的virtual table(s);

4.在那之前,所有上层的base class constructors必须被调用,以base class的声明顺序为序;

5. 在那之前,所有virtual base class constructors必须被调用,从左到右,从最深到最浅。


constructors的调用顺序是:由根源到末端,由内而外。


vptr在构造函数中的初始化时机: 在base class constructors调用操作之后,在程序员提供的代码或是初始化列表中所列的members初始化操作之前;

constructor的执行算法通常如下:

1. 在derived class constructor 中,所有virtual base classes 以及上一层 base class 的constructors会被调用;

2. 上述完成之后,对象的vptr(s)被初始化,指向相关的virtual table(s);

3.如果有member initialization list 的话,将在constructor体内扩展开,这必须在vptr被设定之后才进行,以免有一个virtual member functions被调用;

4.最后执行程序员所提供的码。


vptr保证能够在member initialization list 被扩展之前,由编译器正确设定好,理论上就可以调用虚函数了,但是在语意上这可能是不安全的,因为函数本身可能还得以来被设立初值的class members。



小结
   在合成的默认构造函数中,只有base class subobjects和成员对象会被初始化,所有其它的nonstatic data member,如int,int指针,int 数组等都不会被初始化,这些初始化操作对程序员而言或许有需要,但对编译器则非必要。

   
   Data语意学

一个空的class含有一个大小为1byte的char,使这个class的两个object在内存中分配独一无二的地址。


三个需要考虑的因素
1.语言本身所造成的额外负担,比如虚函数表指针;
2.编译器对于特殊情况所提供的优化处理,比如说空的基类;
3.边界对齐机制。


Data member的布局
非静态成员在classobject中的排列顺序和其被声明的顺序一样,热河中间介入的static data members都不会被放到对象的布局中。各个members并不以低昂得连续排列,只要保证较晚出现的members在class object中有较高的地址。


Data member的存取

    每个staticdata member只有一个实体,存放在程序的data段中。
    如果static data member是一个从复杂继承关系中继承而来的member(虚继承)。程序之中对于static members还是只有唯一一个实体,还是在data段。


None static Data Members
欲对一个nonstatic data member进行存取操作,编译器需要在class object的起始地址加上 data member的偏移量。每个nonstatic data member的偏移量在编译器间就可以获得,因此存取一个nonstatic data member其效率和存取一个c struct member或一个 nonderived class的member是一样的。但是虚函数会曾加一些间接性,存取速度会慢一些。
当point3d是一个derived class,而在其继承结构中有一个virtual base class,而却被存取的member是一个从该virtual base class继承而来的member时,就会有重大差异。
在通过继承类对象访问虚基类对象中的成员(函数/数据)时,这样会增加引用寻址时间,其实就是调整this指针以指向虚基类对象,只不过调整是在运行期间完成的。


继承与Data member

在c++继承模型中,一个derived class object所表现出来的东西,是其自己的members加上其base classes members的总和。
选择某些函数做成inline函数是设计class的一个重要课题。
把一个class分解为两层或更多层,有可能会为了表现class体系的抽象化而膨胀所需空间。


加上多态之后的额外负担
1.导入一个相关的virtual table,用来存放它所声明的每一个virtual functions的地址,这个table的元素的数目一般而言是被声明的virtual functions的数目,再加上一个或两个slots,用于支持RTTI;
2.在每一个class object中导入一个vptr,提供执行器的链接,使得每一个object能够找到相应的virtual tabble;
3.加强constructors,使它能够为vptr设定初值;
4.加强destructor,使之能够删除vptr。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
构造函数是一种特殊的成员函数,用于创建和初始化类的对象。在C++中,有几种不同类型的构造函数。 首先,自定义拷贝构造函数是一种特殊的构造函数,用于创建一个新对象并将其初始化为与另一个对象相同的值。通过在类中定义一个拷贝构造函数,可以自定义如何复制对象的数据成员。例如,可以使用另一个对象的数据成员来初始化新对象的数据成员。\[1\] 其次,普通的构造函数可以有不同的参数,用于初始化对象的数据成员。可以根据需要定义多个构造函数,每个构造函数可以接受不同的参数。这样可以根据提供的参数来选择使用哪个构造函数来创建对象。\[2\] 最后,如果没有显式定义任何构造函数,编译器会为类生成一个默认构造函数。默认构造函数没有参数,并且可以用来创建对象并将其数据成员初始化为默认值。\[3\] 总结来说,构造函数是用于创建和初始化类的对象的特殊成员函数。可以自定义拷贝构造函数、普通构造函数和默认构造函数来满足不同的需求。 #### 引用[.reference_title] - *1* *2* [C++构造函数的各种用法全面解析(C++面向对象编程)](https://blog.csdn.net/Viewinfinitely/article/details/115017678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [C++构造函数、默认构造函数和=default](https://blog.csdn.net/weixin_42108533/article/details/125953755)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值