• 类拓展——拷贝控制成员


    一、拷贝控制操作之于类

    作用:定义类对象拷贝、移动、赋值或销毁时做什么

    没有定义:编译器会为我们定义,但合成版本的行为可能并非我们所想

    二、拷贝构造函数

    1. 每个成员的类型决定了它如何拷贝

    类类型的成员,会使用其拷贝构造函数来拷贝;内置类型的成员则直接拷贝。

    对于数组,合成拷贝构造函数会逐元素地拷贝一个数组类型的成员。

    2. 细节

    第一个参数是自身类类型的引用,且任何额外参数都有默认值

    通常不应该是explicit的,因为它经常会被隐式地使用

    即使定义了拷贝构造函数,编译器也会生成合成版本

    三、拷贝赋值运算符

    1. 工作过程

    将右侧运算对象的每个非static成员了赋予左侧运算对象的对应成员,这通过成员类型的拷贝赋值运算符来完成。

    2. 细节

    类未定义自己的拷贝赋值运算符,编译器会为它合成一个

    合成版本返回一个指向其左侧运算对象的引用

    四、析构函数

    1. 工作过程

    析构函数有一个函数体和一个析构部分,在一个析构函数中,首先执行函数体,然后销毁成员。

    在一个析构函数中,不存在类似构造函数中初始化列表的东西来控制成员如何销毁,析构部分是隐式的。

    如一个合成版本的空析构函数体执行完毕后,成员在隐含的析构阶段中被销毁,如销毁string成员时会调用string的析构函数来释放其所占内存。

    2. 成员销毁时发生什么完全依赖于成员的类型

    类类型的成员在销毁时,执行成员自己的析构函数;内置类型没有析构函数,因此销毁内置类型什么也不需要做

    故隐式销毁一个内置指针类型的成员不会delete它所指向的对象。

    3. 细节

    类未定义自己的析构函数时,编译器会为它合成一个

    五、拷贝构造函数和拷贝赋值运算符

    拷贝/移动构造函数:定义了当用同类型的另一个对象初始化本对象时做什么

    拷贝/移动赋值:定义了将一个对象赋予同类型的另一个对象时做什么

    	string s1(10, 'c');				//直接初始化 
    	//拷贝构造函数 
    	string s2(s1);					//直接初始化 
    	string s3 = s1;					//拷贝初始化 
    	string s5 = string(10, 'a');	//拷贝初始化 
    	//拷贝赋值运算符
    	s2 = s1;						//不是初始化 

    六、三/五法则

    1. 需要析构函数的类也需要拷贝和赋值操作

    如合成版本的析构函数不会释放直接管理的内存,故需要定义一个析构函数来释放构造函数分配的内存

    而如果使用合成版本的拷贝和赋值操作,那么它们将简单拷贝指针成员,造成多个对象指向相同的内存

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

    合成版本不能为每个对象分配一个独有的序号,故我们需要定义拷贝构造函数

    为了避免赋值时将序号赋予目的对象,我们需要定义拷贝赋值运算符

    3. 如果一个类定义了任何一个拷贝操作,它最好定义所有五个操作

    七、显式要求编译器生成合成的版本

    1. 实现

    将拷贝控制成员定义为=default

    2. 细节

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

    在类内用=default声明后,该函数即内联函数

    八、阻止拷贝

    1. 实现

    将拷贝构造函数和赋值运算符定义为删除的函数,将其定义为=delete

    =delete告诉编译器,我们不希望定义这些成员,即不会帮我们合成相应的版本

    2. 细节

    可以对任何函数指定=delete

    析构函数不应被删除

    九、合成的拷贝控制成员可能是删除的

    如果类的某个成员的析构函数是删除的,则类的合成析构函数、合成拷贝构造函数和合成默认构造函数被定义为删除的

    如果类的某个成员的拷贝构造函数是删除的,则类的合成拷贝构造函数被定义为删除的

    如果类的某个成员的拷贝赋值运算符是删除的,或是类有一个const的或引用成员,则类的合成拷贝赋值运算符被定义为删除的

    如果类有一个引用成员且其没有类内初始值,或有一个const成员且其没有类内初始值并且未显式定义默认构造函数,则其默认构造函数被定义为删除的

    十、移动操作

    1. 特点

    移动构造函数不分配任何新内存,它接管给定参数的内存,即直接接管目标对象的资源,而不需要拷贝

    2. 实现

    第一个参数是该类类型的一个右值引用

    通常加上noexcept关键字以表明该函数不会抛出异常

    3. 细节

    如果一个类没有移动操作,通过正常的函数匹配,类会使用对应的拷贝操作来代替

    只有一个类没有定义任何自己版本的拷贝控制成员,且类的每个非static数据成员都可以移动时,编译器才会生成相应的合成版本

    移动操作永远不会定义为删除的函数

  • 相关阅读:
    js基础四
    序列化和反序列化
    数组
    枚举
    Class对象和反射
    字符串String
    对象的克隆
    异常处理机制
    多继承和代码块
    接口和抽象类
  • 原文地址:https://www.cnblogs.com/xzxl/p/7820450.html
Copyright © 2020-2023  润新知