只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这样的构造函数叫做复制构造函数(有时也称为拷贝构造函数),例如:

class Person{

public:

Person();//默认构造函数

Person(const Person&);//复制构造函数

....................

};

与默认构造函数一样,复制构造函数可由编译器隐式调用,它主要用于以下几种情况:

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

(2)复制一个对象,将它作为实参传给一个函数

(3)从函数返回时复制一个对象

(4)初始化顺序容器中的对象

(5)根据元素初始化式列表初始化数组元素

C++支持两种初始化形式:直接初始化和复制初始化,复制初始化使用=符号,而直接初始化将初始化式放在圆括号中,直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定构造函数创建一个临时的对象,然后用复制构造函数将那个临时的对象复制到正在创建的对象。例如:

string book="9-999-99999-100";

上式创建book时,编译器首先调用接受一个C风格字符串形参的string构造函数,创建一个临时的对象,然后编译器使用string复制构造函数将book初始化为那个临时对象的副本。

当形参为非引用类型的时候,将复制实参的值,类似的,以非引用类型作返回值时,将返回return语句中的值的副本,因此,当形参或者返回值为类类型时,将由复制构造函数进行复制;复制构造函数可以用于初始化顺序容器中的元素,例如:

vector<string> svec(5);

容器的这种构造方式使用了默认构造函数和复制构造函数。编译器首先使用string默认构造函数创建一个临时值svec,然后使用复制构造函数将临时值复制到svec的每个元素;如果没有为类类型数组提供元素初始化式,则将用默认构造函数初始化每个元素,不过,如果使用常规的花括号括住的数组初始化列表来提供显式元素初始化式,则使用复制初始化开初始化每个元素。编译器根据指定值创建适当类型的元素,然后用复制构造函数将该值复制到相应元素:

string book[]={ string("0-201-201"),

string("0-201-202"),

string("0-201-203")

};

如果我们没有定义复制构造函数,编译器就会为我们合成一个,不过,与合成的默认构造函数不同,即使我们定义了其他构造函数,也会合成复制构造函数。合成复制构造函数将执行逐个成员初始化,完成对象之间的位拷贝(位拷贝又称浅拷贝),将新对象初始化为原对象的副本。所谓的“逐个成员”在,指的是编译器将现有对象的每个非static成员,依次复制到正在创建的对象。合成复制构造函数直接复制内置类型成员的值,类类型成员使用该类的复制构造函数进行复制。数组成员的复制是个例外,虽然一般不能复制数组,但如果一个类具有数组成员,则合成复制构造函数将复制数组,而且合成复制构造函数将复制数组的没一个元素。

对于许多类来讲,合成复制构造函数只完成必要的工作,只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显示定义复制构造函数,也可以复制,这种情况叫做“浅拷贝”。不过,有些类必须对复制对象时发生的事情加以控制,这样的类经常有一个数据成员是指针,或者有成员表示在构造函数中分配的其他资源,而也有一些类在创建新对象时必须做一些特定工作,这些情况下,都必须定义复制构造函数,这种情况下叫“深拷贝”。

对于普通类型的对象来说,它们之间的复制是很简单的,例如:

int a=100;
int b=a;
而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。下面看一个类对象拷贝的简单例子。

#include <iostream>
using namespace std;
class Test
{
 public:
  Test(int b)
  {
   a=b;
  }
  void Show ()
  {
   cout<<a<<endl;
  }
 private:
  int a;
};

int main()
{
 Test A(100);
 Test B=A;
 B.Show ();
 return 0;
}
  运行程序,屏幕输出100。从以上代码的运行结果可以看出,系统为对象B分配了内存并完成了与对象A的复制过程。就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。

浅拷贝和深拷贝

  在某些状况下,类内成员变量需要动态开辟堆内存,假如实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,假如B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

  深拷贝和浅拷贝可以简单理解为:假如一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。下面举个深拷贝的例子。

#include <iostream>
using namespace std;
class Test
{
 public:
  Test(int b,char* cstr)
  {
   a=b;
   str=new char[b];
   strcpy(str,cstr);
  }
  Test(const Test& C)
  {
   a=C.a;
   str=new char[a]; //深拷贝
   if(str!=0)
    strcpy(str,C.str);
  }
  void Show()
  {
   cout<<str<<endl;
  }
  ~Test()
  {
   delete str;
  }
 private:
  int a;
  char *str;
};

int main()
{
 Test A(10,"Hello!");
 Test B=A;
 B.Show();
 return 0;
}

因此,复制构造函数也叫做拷贝构造函数,它分为浅拷贝,一般情况下,浅拷贝就合成复制构造函数可以完成,另外一种是深拷贝,也就是需要我们显示定义复制构造函数的一种情况了。

如果有些类需要完全禁止复制,那么只需将复制构造函数声明为私有成员函数即可,否则如果不声明定义,编译器将会默认合成一个复制构造函数,照样可以完成浅拷贝的复制构造过程,如:

#include <iostream>

using namespace std;

class Obj
{
public:
    Obj()
    {
        cout << "默认构造函数" << endl;
    }
private:
    Obj(const Obj &obj)
    {
        cout << "复制构造函数" << endl;
    }
};

int main()
{
    Obj obj1;
    Obj obj2(obj1);

return 0;
}
编译报错:

error: 'CObj::CObj(const CObj&)' is private

这就是禁止复制的效果,正是这里想要的。

如果想要连友元函数和成员函数的复制也禁止,那么可以声明一个私有的复制构造函数但不对其定义。因为友元函数或成员函数可以访问到类的私有成员,所以当然能调用私有的复制构造函数,所有将复制构造函数声明为私有但不定义,就能避免友元或成员函数的调用。声明而不定义成员函数是合法的,但是,使用未定义成员的任何尝试将导致链接失败。用户代码中的复制尝试将在编译时标记为错误,而成员函数和友元中的复制尝试将在链接时导致错误。

#include <iostream>

using namespace std;

class Obj
{
public:
    Obj()
    {
        cout << "默认构造函数" << endl;
    }
    friend void fri_copy();
private:
    Obj(const Obj &obj);
};

void fri_copy()()
{
    CObj obj1;
    CObj obj2(obj1);
}

int main()
{
     fri_copy();

return 0;
}
编译报错:
undefined reference to `CObj::CObj(CObj const&)'
这也正是这里需要的结果,禁止复制成功。

C++之复制控制的更多相关文章

  1. C++ 复制控制之复制构造函数

    7月26日更新: 过了这么长的时间回过头来看,发现文章中有几个点说错(用红字标出): 构造函数不是只有唯一一个参数,它也可以是多参数形式,其第二参数及后继以一个默认值供应. 不是没有声明复制控制函数时 ...

  2. C++ Primer 学习笔记_67_面向对象编程 --转换与继承、复制控制与继承

    面向对象编程 --转换与继承.复制控制与继承 I.转换与继承 引言: 由于每一个派生类对象都包括一个基类部分,因此能够像使用基类对象一样在派生类对象上执行操作. 对于指针/引用,能够将派生类对象的指针 ...

  3. C++ Primer 随笔 Chapter 13 复制控制

    1.复制控制包含的内容:复制构造函数.赋值操作符.析构函数 2.复制构造函数: a. 定义:只有单个形参,而且该形参是对本类类型的引用,这样的构造函数被成为复制构造函数 b. 适用情况: (1)根据一 ...

  4. C++Primer笔记之复制控制

    复制控制这一节需要注意的地方不多,主要有以下几点: 1.定义自己的复制构造函数 什么时候需要定义自己的复制构造函数,而不用系统提供的,主要遵循以下的经验说明: 某些类必须对复制对象时发生的事情加以控制 ...

  5. 稍微深入点理解C++复制控制【转】

    通过一个实例稍微深入理解C++复制控制过程,参考资料<C++ primer>,介绍点基本知识: 1.在C++中类通过特殊的成员函数:复制构造函数.赋值操作符和析构函数来控制复制.赋值和撤销 ...

  6. C++拾遗(六)——复制控制

    年前忙了几天,到现在才算是有空休息下来.先祝大家新年快乐,心想事成:)我也会发笑脸o.o 这篇博文主要介绍定义一个类型的对象时的复制控制方式,这部分内容之前有一定的了解但又浅尝辄止,始终感觉没能找到要 ...

  7. C++继承与构造函数、复制控制

    每个派生类对象由派生类中定义的(非static)成员加上一个或多个基类子对象构成,因此,当构造.复制.赋值和撤销派生类型对象时,也会构造.复制.赋值和撤销这些基类子对象. 构造函数和复制控制成员不能继 ...

  8. C++复制控制

    1.复制构造函数可用于: (1)根据另一个同类型的对象显示或隐式初始化一个对象 string str1="test";   //隐式 string str2=str1; //显示 ...

  9. C++复制控制:拷贝构造函数

    一.拷贝构造函数是一种特殊构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用.与默认构造函数一样 ,拷贝构造函数可由编译器隐式调用.拷贝构造函数应用的场合为: (1)根据另一个同类 ...

随机推荐

  1. memcached安装与启动

    windows 安装1.4.4版本 https://pan.baidu.com/s/1xX1NThLqeq2zNMaqONFgkQ 解压,“以管理员身份” 运行cmd,切换到memcached根目录, ...

  2. WPF 资源应用

    对资源的应用,有好多方法,以下是一些应用,可以参考 1.静态资源: 2.动态资源: 3.项目面板中的资源: 4.图片.声音等资源

  3. java获取时间整点工具代码

    /**获取上n个小时整点小时时间 * @param date * @return */ public static String getLastHourTime(Date date,int n){ C ...

  4. Qt——树结点的搜索

    一.Qt中的树 平时我们经常使用树的结构来组织和展示数据,比如文件系统等—— 在Qt中,我们可以使用Qt提供的便捷的QTreeWidget类,利用该类的接口,轻松地将已有数据显示在树中. 除此之外,还 ...

  5. HTML表单之input元素的23种type类型

    摘自:http://www.cnblogs.com/xiaohuochai/p/5179909.html 了解HTML表单之input元素的23种type类型 随着HTML5的出现,input元素新增 ...

  6. 在Windows*上编译Tensorflow教程

    背景介绍 最简单的 Tensorflow 的安装方法是在 pip 一键式安装官方预编译好的包 pip install tensorflow 通常这种预编译的包的编译参数选择是为了最大兼容性而不是为了最 ...

  7. BZOJ1087:[SCOI2005]互不侵犯——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=1087 Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王 ...

  8. lamp 源码安装

    #!/bin/bash #description:mysql-.tar apache2.4.23 php5.6.27 function check_ok(){ ] then echo "-- ...

  9. HDU 4549 矩阵快速幂+快速幂+欧拉函数

    M斐波那契数列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Sub ...

  10. [LeetCode] 4. Median of Two Sorted Arrays ☆☆☆☆☆

    There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two ...