C++对象模型二——构造语义

构造函数语意

默认构造

  1. 编译器在需要的时候会合成默认构造,被合成的构造只执行编译器所需要的行动,像一些数据成员的初始化操作并不执行,因为那是程序员的职责。下面是几种默认构造合成的情况:
  • 成员变量中存在,带有默认构造的对象
    • 这个合成仅仅会在构造真正需要被调用的时候才会发生
    • 如果有多个成员都要求构造函数初始化,按照class中的声明顺序来,在用户代码之前调用成员的构造
  • 带有默认构造的基类:一个没有任何构造的类派生自一个有默认构造函数的基类,需要被合成出来调用基类的默认构造
    • 如果子类有除了默认构造之外的构造,编译器会扩张现有的一个构造,将调用所有必要的默认构造的代码加进去
    • 如果存在带默认构造的成员,先基类构造,再成员默认构造
  • 带有一个虚函数的类,两种情况下需要合成默认构造:(因为需要初始化虚表指针)
    • class声明或者继承自一个虚函数
    • class派生自一个继承串,其中有一个或更多的虚基类
  • 带有一个虚基类的类,通过派生类访问成员时需要知道编译器产生指针指向虚基类
  1. 显式定义默认构造:T() = default
  2. 两个误解:都是不成立的
    • 任何class如果没有定义构造函数,编译器都会合成
    • 编译器合成的构造函数会明确class内的每一个数据成员的默认值
  3. 不支持静态构造函数
  4. 构造函数不能被声明成常量成员函数
  5. 避免使用默认构造函数:一个类如果没有缺省构造,在三个方面会有问题:
  • 不能建立对象的数组,可以利用指针数组代替,但是有缺点:增加了内存分配量,可以为数组分配原始内存,这样就避免了浪费内存
  • 无法在许多基于模板的容器里面使用——通过仔细设计模板可以杜绝缺省构造函数的使用
  • 不提供缺省构造的虚基类很难与其进行合作

拷贝构造

决定一个cpctor能否被编译器合成的标准在于class能否展现出位逐次拷贝,如果能展现出位逐次拷贝,就不需要合成

  • 有三种情况会调用cpctor(note:后两种不是调用拷贝复制运算符=):
    • 一个对象初始化另一个
    • 参数值传递
    • 返回值拷贝
  • 一个类什么时候不展示位逐次拷贝(要合成cpctor)?前两种情况将合成的构造安插到拷贝构造中
    • 当class有一个成员对象,且这个成员对象有一个cpctor时(不管这个cpctor是明确定义还是编译器合成的)
    • 当class继承自一个基类,且基类有cpctor(不管这个cpctor是明确定义还是编译器合成的)
    • 当class声明了一个或多个虚函数的时候,需要cpctor构造初始化虚表指针vptr
    • 当class派生自一个继承串链,其中有一个或者多个虚基类
  • 拷贝构造要还是不要?
    • 如果支持位拷贝,不需要提供拷贝构造,因为编译器实施了最好的行为,既快速又安全
    • 如果需要拷贝构造,使用memcpy会更有效率,不管使用memcpy还是memeset,都只在class不含任何编译器产生的内部成员时才安全,如果又虚函数或者虚基类就不安全
    • 类的拷贝操作在什么情况下可以直接使用memcpy?
      • 拷贝构造函数通常不应该是explicit的,因为如果没有拷贝赋值运算符,=会被转换位拷贝构造的调用
      • 关于拷贝构造和拷贝运算符:基本上声明时会使用拷贝构造函数,赋值语句会使用拷贝运算符
MyClass a{5}; // 拷贝构造
MyClass b = a; // 拷贝构造   因为这也是声明
a = b;  // 赋值  operator=

程序转化语义

  • 明确的初始化操作:
    • 重写每一个定义,其中的初始化操作会被剥离
    • class的拷贝构造的调用操作会被安插进去
  • 参数的初始化
  • 返回值的初始化,以下例子也是在编译层面做优化NRV
X bar()
{
	X xx;
	return xx;
}
// 转化过程(可能每个编译器的实现不一样)
  1. 首先加一个额外参数,类型为对象的引用;
  2. 在return之前安插一个拷贝构造操作,return 空(这就是编译器省略拷贝构造的唯一一种情况)
void bar(X& _result)
{
	X xx;
	xx.X::X();
	_result.X::X(xx);
	return;
}

如果是函数指针也是同理

  • 在程序员层面优化:
X bar(const T& y, const T& z){
    X xx;
    return xx;
}
// 这个会要求xx被membersize地拷贝到编译器所产生的结果中,使用以下替代:
X bar(const T& y, const T& z){
    return X(y, z);
}
// 效率会比较高,避免了一次拷贝构造:
void bar(X& __result, const T& y, const T& z){
    __result.X::X(y, z);
    return;
}

成员函数的初始化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值