C++ Primer学习笔记——$13 拷贝控制

转载 2011年01月19日 15:08:00

题记:本系列学习笔记(C++ Primer学习笔记)主要目的是讨论一些容易被大家忽略或者容易形成错误认识的内容。只适合于有了一定的C++基础的读者(至少学完一本C++教程)。

 
作者: tyc611, 2007-01-29
   本文主要讨论C++类定义中的拷贝控制(copy control):拷贝构造函数、赋值操作符和析构函数。
   如果文中有错误或遗漏之处,敬请指出,谢谢!
   C++类中有四个不可或缺的部分,那就是构造函数、拷贝构造函数、赋值操作符和析构函数。如果类中没有定义这些函数,那么编译器将为类自动生成这些函数。当然,你也可以通过private控制策略限定不使用拷贝构造函数和赋值操作符。
 
   其中,拷贝构造函数、赋值操作符和析构函数总称为拷贝控制(copy control)。
 
   当类中有指针类数据成员时,一般都需要自已实现类的拷贝控制。通常有两种处理策略:一是定义值型类,每个类保留一份指针指向的对象的拷贝;另一种更常用的策略是使用智能指针(smart pointer),其通用技术是采用引用计数(reference count)来实现共享指针指向的对象。
 
拷贝构造函数
 
   我们知道C++中变量初始化有两种形式:直接初始化和拷贝初始化。直接初始化将初始化放在圆括号中,而拷贝初始化使用=符号。对于内置类型,这两者基本上没有区别。但对于类类型,两种方式是有区别的:直接初始化直接调用与实参匹配的构造函数;而拷贝初始化总是调用拷贝构造函数,具体而言,就是拷贝初始化首先使用指定构造函数创建一个临时对象,然后用拷贝构造函数将那个临时对象拷贝到正在创建的对象。
 
   支持拷贝初始化主要是为了与C的用法兼容。当情况允许时,可以允许编译器跳过拷贝构造函数直接创建对象,但编译器没有义务这样做。注:事实上大多数编译器都跳过了拷贝构造函数,因为这完全可以跳过,比如在VC6.0和MinGW2.05。
 
   我们知道可以用表示容量的单个参数来初始化容器,容器的这种构造方式使用了默认构造函数和拷贝构造函数。例如:
   vector<string> svec(5);
编译器首先使用string的默认构造函数创建一个临时值来初始化svec,然后使用拷贝构造函数将临时对象拷贝到svec的每个元素。示例代码如下:
#include <iostream>
#include <vector>
using namespace std;

class Test {
public:
    Test() {
        cout<<"Contructor"<<endl;
    }
    Test(const Test& t) {
        cout<<"copy Contructor"<<endl;
    }
};

int main() {
    vector<Test> tvec(5);
    return 0;    
}

输出结果为:(MinGW 2.05和VC6.0)
Contructor
copy Contructor
copy Contructor
copy Contructor
copy Contructor
copy Contructor

 
   对于元素为类类型的数组,可以使用数组初始化列表来提供显示元素初始化。此时,使用拷贝初始化来初始化每个元素。根据指定值创建适当类型的元素,然后用拷贝构造函数将该值拷贝到相应元素。当然,同前面一样,是否跳过拷贝构造函数取决于编译器(事实上,大多数编译器跳过了这步)。
 
   拷贝构造函数的形参是一个类类型引用(否则,参数本身就需要过拷贝构造函数了),但一般情况下,我们使用const修饰。并且,一般不应该设置为explicit。
 
   有时需要禁止拷贝类,例如,iostream类就不允许拷贝。这时,应当显示声明拷贝构造函数为private,此时可以不定义该函数。若声明为private且进行了函数定义,则类的友元和成员仍然可以进行拷贝。
 注意:声明而不定义成员函数是合法的,但是,使用未定义成员的任何尝试将导致链接失败。
 
合成的拷贝构造函数
 
   如果我们没有定义拷贝构造函数,则编译器会自动生成一个,把这个自动生成的拷贝构造函数叫合成的拷贝构造函数(Synthesized Copy Constructor)。合成的拷贝构造函数执行逐个成员初始化,将新对象初始化为原对象的副本。如果成员是内置类型,则执行位拷贝;如果成员是类类型,则调用相应的拷贝构造函数;如果成员是数组类型,则分别对每个数组元素进制拷贝。
 
赋值操作符
 
   赋值操作符的右操作数一般以const引用传递,为了与内置类型的行为一致,常返回该类类型的引用。通常,拷贝构造函数和赋值操作符是同时出现的,定义了一个就应当定义另一个。
 
  赋值操作符一个必须小心的地方时检查自赋值。
 
析构函数
 
   三法则(rule of three):如果类需要自定义析构函数,则它往往也需要拷贝构造函数和赋值操作符。
 
   合成的析构函数按成员在类中声明次序的逆序来撤销成员。
 
   析构函数与拷贝构造函数或者赋值操作符之间的一个重要区别是:即使我们编写了自己的析构函数,在自定义析构函数运行结束后,合成析构函数仍然将继续运行,它来完成成员数据的撤销工作。
 

   如果文中有错误或遗漏之处,敬请指出,谢谢!


 

参考文献:
[1] C++ Primer(Edition 4)
[2] Thinking in C++(Volume Two, Edition 2)
[3] International Standard:ISO/IEC 14882:1998

相关文章推荐

C++ Primer学习笔记(13)——封装、继承、多态

C++ 是一种典型的面向对象的编程语言,其最显著地特点是封装、继承和多态。充分理解封装、继承、多态是如何实现的,学好C++就不是难事了。...

读书笔记:C++ primer 5th edition--chapter13.拷贝控制

part1.拷贝、赋值与销毁 1.拷贝构造函数,拷贝赋值运算符,移动构造函数,移动赋值运算符,析构函数对应的操作称为拷贝控制操作 2.拷贝构造函数 1)第一个参数必须为引用类型。因为在函数调用过...
  • lebula
  • lebula
  • 2016年10月12日 01:32
  • 167

《c++ primer》 第13章 拷贝控制 学习笔记

第 13 章 拷贝控制 1.拷贝,赋值与销毁 对初学c++的程序员来说,必须定义对象拷贝,移动,赋值或销毁时做什么。 拷贝构造函数:如果一个构造函数的参数是本身的引用,且其他的参数都有默认值,此...

C++primer学习笔记----拷贝控制

一个类通过定义五种特殊的成员函数来控制拷贝控制操作:拷贝构造函数、拷贝赋值运算符、移动构造函数、移动赋值运算符、析构函数。...

c++ primer(第五版)学习笔记及习题答案代码版(第十三章)拷贝控制

笔记较为零散,都是自己不熟悉的知识点。 习题答案至于一个.h 和.cc 中,需要演示某一题直接修改 #define NUM****, 如运行13.30题为#define NUM1330; chapt...
  • refuil
  • refuil
  • 2016年05月31日 17:50
  • 1343

2012/1/13 《C++ Primer Plus》第五章:循环和表达式 学习笔记

《C++ Primer Plus》第五章学习笔记 57:C++表达式是值或值与操作符的组合,每个C++表达式都有值。   58:++x; x++;这两条语句作用效果相同。对于内置类型,不会有差别...
  • Zyearn
  • Zyearn
  • 2012年01月14日 17:09
  • 516

《C++ Primer》读书笔记——第十三章_拷贝控制

一个类有5种特殊的成员函数:拷贝构造函数、拷贝赋值运算符、覅用构造函数、移动赋值运算符、析构函数。如果没有定义这些拷贝控制成员,编译器会自动为它定义缺失的操作。 13.1  拷贝构造函数 如果一个...

重读C++ Primer,记录一些之前不易觉察的知识点(13. 拷贝控制)

1.复制构造函数 如果我们没有定义复制构造函数,编译器就会为我们合成一个。即使我们定义了其他构造函数,虽然不会再生成默认构造函数,但是也会生成默认复制构造函数(合成复制构造函数)。合成复制构造函数的行...
  • c77_cn
  • c77_cn
  • 2013年09月04日 13:09
  • 476

C++primer第五版【学习笔记】——第一章

1.前言 一直以来都习惯用C写程序,对C++的了解,虽然在大学本科时期学过最基本的一些类,继承,多态的机制,但是很少真正去编程序。最近拿到第五版的C++primer,里面加入了最新的C++11标准,...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++ Primer学习笔记——$13 拷贝控制
举报原因:
原因补充:

(最多只允许输入30个字)