第13章 拷贝控制

13.1 拷贝控制

13.1.1 拷贝构造函数

1、如果我们没有定义拷贝构造函数,编译器会为我们定义一个拷贝构造函数

2、合成的拷贝构造函数默认会将其参数的每个非static成员逐个拷贝到正在创建的对象中

3、直接初始化与拷贝初始化

  • 直接初始化要求编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数
  • 拷贝初始化要求编译器将右侧运算对象拷贝到正在创建的对象中,如果需要的话还要进行类型转换
  • 拷贝初始化依靠拷贝构造函数或者移动构造函数来实现

4、拷贝初始化发生情况

  • 使用=定义变量时
  • 将一个对象作为实参传递给一个非引用类型的形参
  • 从一个返回类型为非引用类型的函数返回一个对象
  • 用花括号列表初始化一个数组中的元素或一个聚合类(即C中的结构体)中的成员

13.1.2 拷贝赋值运算符

1、赋值运算符满足右结合定律,通常应该返回一个指向其左侧运算对象的引用

2、如果一个类为定义自己的赋值运算符,编译器会生成一个合成拷贝赋值元算符,会将右侧运算对象的每个非static成员赋予左侧运算对象的对应成员

13.1.3 析构函数

1、在一个构造函数中,成员的初始化是在函数体执行之前完成的,且按照它们在类中出现的顺序进行初始化;在一个析构函数中,首先执行函数体,然后销毁成员,成员按照初始化顺序的逆序销毁

2、析构函数本身并不直接销毁成员,成员是在析构函数体之后隐含的析构阶段中被销毁的

13.1.4 三/五法则

1、如果一个类需要自定义析构函数,大概率这个类也需要自定义拷贝赋值运算和拷贝构造函数

2、需要拷贝操作的类也需要赋值操作,反之亦然

13.1.5 =default & =delete

1、=default只能对具有合成版本的成员函数使用,即默认构造函数和拷贝控制成员

2、=default支持在类内声明或类外定义时修饰类成员函数,在类内声明时添加,合成的函数将隐式的声明为内敛的

3、=delete必须出现在函数第一次声明的时候

4、=delete可以修饰任何类成员函数

13.6 对象移动

13.6.1 右值引用

1、左值持久,右值短暂;

2、std::move接口可将一个左值转换为右值引用类型,对于一个使用了std::move的左值,除了对其进行赋值或者销毁外,我们不再使用它

13.6.2 移动构造函数和移动赋值运算符

1、移动构造函数

  • 除了完成资源移动,移动构造函数还必须确保移后对象出于这样一个状态--必须可析构、可重新赋值
  • 与拷贝构造函数不同,移动构造函数不分配任何新内存
  • 不抛出异常的移动构造函数和移动赋值运算符必须标记为noexcept,noexcept需要在声明和定义时都指定,显示的告诉编译器此移动构造函数或者移动赋值运算符可安全使用,不会抛出异常
Strvec::Strvec(Strvec &&s) noexcept 
    //成员初始化器接管s中的资源
    : elements(s.elements), first_free(s.first_free), cap(s.cap) {
    //令s为对其析构是安全的
    s.elements = s.first_free = s.cap = nullptr;
}

2、移动赋值运算符

Strvec& Strvec::operator=(Strvec &&rhs) noexcept {
    //检测自赋值,当前右值可能是move调用返回的结果
    if (this != &rhs) {
        free(); //释放已有元素
        elements = rhs.elements;
        first_free = rhs.first_free;
        cap = rhs.cap;
        //将rhs置于可析构的状态,通过将移后源对象的指针成员置为nullptr实现
        rhs.elements = rhs.first_free = rhs.cap = nullptr;
    }
    return *this;
}

3、合成的移动操作

  • 如果一个类定义了自己的拷贝构造函数、拷贝赋值运算符或者析构函数,编译器不会自动合成移动构造函数和移动赋值运算符
  • 只有当一个类没有定义任何自己版本的拷贝控制成员,且它所有数据成员都能移动构造或者移动赋值时(编译器可以移动内置类型,类类型由其移动操作完成),编译器才会合成移动构造函数和移动赋值运算符
  • 定义一个移动构造函数或者移动赋值运算符的类必须定义自己的拷贝操作,否则这些成员默认被定义为删除的

4、移动操作使用右值,拷贝操作使用左值,但如果没有移动构造函数,右值也会被拷贝(即使使用了std::move转换为右值也会调用拷贝操作)

13.6.3 右值引用和成员函数

void push_back(const X&);    //拷贝,绑定到任意类型的X
void push_back(X&&);         //移动,只能绑定到类型X的可修改的右值

1、左值和右值引用成员函数

  • 我们使用引用限定符(可以是&或者&&)分别指出this可以指向一个左值或右值
  • 类似const限定符,引用限定符只能用于非static成员函数,且必须同时出现在函数的声明和定义中
  • 一个函数可以同时使用const和引用限定符,且限定符必须出现在const之后
  • 就像一个成员函数可以根据是否有const来区分其重载版本一样,引用限定符也可以区分重载版本
  • 如果一个成员函数有引用限定符,则具有相同参数列表的所有版本都必须有引用限定符

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值