复制控制(C++Primer_第13章)

C++复制控制

一、复制控制

类能控制复制,赋值,撤销该类的对象时的动作,分别通过下面的成员函数:

  1. 复制构造函数:具有单个形参,该形参是对该类类型的引用(通常用 const 修饰);

  2. 赋值操作符

  3. 析构函数:不管有没显示定义,编译器都自动执行类中非 static 数据成员的析构函数

这三个函数就成为复制控制。

二.为什么要研究复制控制

如果没有显示地定义复制构造函数和赋值操作符,编译器会为我们定义。

但是编译器合成的复制控制函数只做必需的工作,某些类如果依赖于默认的定义会导致错误,例如类具有指针成员。

难点: 识别何时需要覆盖默认版本,定义自己的复制构造函数

所以有时候要自定义复制构造函数,定义复制构造函数跟构造函数是一样的,难点是能认识到需要定义复制构造函数。

(具体请看第五点:” 什么时候要自己的复制构造函数”)

三.复制构造函数会几时用到?

首先:区分构造函数和复制构造函数

1. 根据另一个同类型的对象显式或者隐式地初始化一个对象

区别下面:

(1)下面的构造函数和复制构造函数仅在低级别优化上存在差异

String str3(10,“a”); // 调用构造函数

String str4 ; // 调用构造函数

String str1= “hello”; // 调用复制构造函数

String str2 = string(); // 调用复制构造函数

(2)对于不支持复制的类型,或者使用非 explicit 构造函数的时候,它们有本质区别:

ifstream file1(“filename”); //OK, 调用构造函数

ifstream file2 = “filename” ; //ERROR, 不能复制 IO 类型的对象

//(复制构造函数是 private 的)

Sales_item item = string(“hello”); // 取决于构造函数是不是 explicit,如果构造函数 是显式的,则初始化失败,如果构造函数是隐式的,则初始化成功。

2. 一个对象作为实参传给一个函数,或者作为函数的返回值

例如: string fun(const stirng& A, const string B);

返回值和 第二个参数 B 隐式地调用复制构造函数;第一个参数 A, 是 const 引用,不能复制。

3. 初始化顺序容器中的元素

当用表示容量的单个形参来初始化容器的时候,用到了默认构造函数和复制构造函数。

vector svec(5);

先用 string 默认构造函数创建一个临时值来初始化 svec, 然后是使用复制构造函数将临时值复制到 svec 的每一个元素。

4. 根据 “元素初始化列表 " 初始化数组元素

(1)如果没有为类类型数组提供元素初始化式,是调用默认构造函数初始化每个元素,

sale_item ClassArray[];

(2)如果用花括号扩展的数组初始化列表来提供显式元素初始化,则是用复制构造函数.

根据指定值创建适当类型的元素,然后用复制构造函数将该值复制到相应的元素。

Sale_item ClassArray2[] = { string(“ab”), // 直接指定一个值,调用单实参构造函数

​ string(“cd”),

​ Sale_item(), // 使用完整的构造函数语法(0 或者多个实参)

};

备注:可以直接指定一个值,调用元素类型的单实参构造函数,如前两个元素的初始化;

如果不指定实参或者指定多个实参,就需要使用完整的构造函数语法,如最后一个元素的初始化

四.编译器合成的复制构造函数

前面提到,如果我们没有定义,编译器会默认合成一个复制构造函数,该函数会对该对象的每一个非 static 成员依次复制到正创建的对象。

其中,不同类型的复制如下:

1. 直接复制内置类型成员的值(不是指针)

2. 类型成员使用该类的复制构造函数

3. 数组复制数组的每一个元素

等价于: 一个构造函数,每个数据成员在构造函数初始化列表中进行初始化;

例如 有三个数据成员的类的合成构造函数:

Sales_item :: Sales_item(const Sales_item &orig) :A(orig.A),B(orig.B),C(orig.C){}

五.什么时候要自己定义复制构造函数(重点)

有些类必须对复制对象时发生的事情进行控制,例如

1、类中有数据成员是指针或者有成员在构造函数中分配其他资源

2、在创建对象时必须做一些特定工作

以上的情况都必须定义复制构造函数。

六、怎么定义复制构造函数(同构造函数一样)

同类同名,没有返回值,可以使用构造函数初始化列表初始化新创建对象的成员,可以在函数体中任何其他必要的工作。

七、如何禁止复制

例如:iostream 类就不允许复制

  1. 如果不自定义复制构造函数,编译器也会自动合成一个,无法禁止复制

  2. 为了防止复制,类必须显式地声明其复制构造函数为 private(其友元和成员依然可以复制)

  3. 如果要连友元和成员的复制也禁止,就可以声明一个 private 的复制构造函数但不对它进行定义。(如果复制类对象会提示编译错误,如果成员和友元尝试复制就会导致链接错误)

八、赋值操作符

1. 如果没有定义自己的赋值操作符,编译器也会自动合成一个(类似于合成的复制构造函数,进行各个成员的赋值)

2. 如果类自定义自己的复制操作符,那么一般类也需要自定义赋值操作符(具体的请看后面的例子)

九、析构函数

1、 析构函数通常用于释放在构造函数或者在对象生命期内获取的资源。析构函数可以执行任意操作,一般是在类对象使用完毕之后要执行的动作。

三法则: 如果类需要析构函数,则它也需要复制操作符和复制构造函数。

2、合成的析构函数按创建的逆序撤销每个非 static 成员。(并不删除指针成员所指向的对象),无论有没创建自定义函数,编译器都会合成析构函数

3、注意:即便自定义了自己的析构函数,编译器在运行自定义析构函数之后,还会运行合成析构函数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值