拷贝构造函数的功能是用一个已有的对象初始化一个被创建的同样对象,是一种特殊的构造函数,具有一般构造函数的所有特性,当创建一个新对象的时候系统会自动调用它;其形参是本类对象的引用,它的特殊功能是将参数代表的对象逐域拷贝到新创建的对象中。

用户可以根据实际需要定义特定的拷贝构造函数,以实现同类对象之间数据成员的传递。如果用户没有声明类的拷贝构造函数,系统会自动生成一个默认的拷贝构造函数,它的功能就是把初始对象的每个数据成员的值复制到新建立的对象当中。它定义为:类名(类名&对象名)

 class dog
{private:int age;float weight;char *color;public:dog();dog(dog&);void play();void hunt();
};dog::dog(dog&other)
{age   = other.age;weight = other.weight;color  = other.color;
}

在以下四种情况下会调用拷贝构造函数

(1)用类的一个对象去初始化另一个对象:

dog dog1;

dog dog2(dog1);

(2)用类的一个对象去初始化另一个对象的另一种形式:

dog dog2 = dog1;

(3) 对象作为参数传递,调用拷贝构造函数:

f(dog a){}

dog b;

f(b);

(4)如果函数的返回值是类的对象,函数调用返回时,调用拷贝构造函数

dog f{

dog a;

.....

return a;

}

dog b;

b = f();

// 拷贝构造函数分为深拷贝和浅拷贝

浅拷贝只是复制对象的空间而不复制资源,深拷贝需要同时复制对象空间和资源。

浅复制:只是对指针的复制,复制后两个指针指向同一个内存空间(*p ---> 内存空间A <---*q)

深复制:不但对指针复制,而且对指针指向的内容进行拷贝,经深复制后的指针指向两个不同地址的指针(*p ---> 内存空间A ; *q ---> 内存空间B)

-----------------------------------------------------------------------------------------------------------------------------

C++构造函数详解(复制构造函数)

构造函数是干什么的

该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数,由构造函数完成成员的初始化工作,故:构造函数的作用:初始化对象的数据成员。

构造函数的种类

 1 class Complex
 2 {
 4 private :
 5     double m_real;
 6     double m_imag;
 7
 8 public:
 9
10     // 无参数构造函数
11     // 如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空,什么都不做
12     // 只要你写了一个下面的某一种构造函数,系统就不会再自动生成这样一个默认的构造函数,如果希望有一个这样的无参构造函数,则需要自己显示地写出来
13     Complex(void)
14     {
15          m_real = 0.0;
16          m_imag = 0.0;
17     }
18
19     // 一般构造函数(也称重载构造函数)
20     // 一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理)
21     // 例如:你还可以写一个 Complex( int num)的构造函数出来
22     // 创建对象时根据传入的参数不同调用不同的构造函数
23     Complex(double real, double imag)
24     {
25          m_real = real;
26          m_imag = imag;
27      }
28
29     // 复制构造函数(也称为拷贝构造函数)
30     // 复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中
31     // 若没有显示的写复制构造函数,则系统会默认创建一个复制构造函数,但当类中有指针成员时,由系统默认创建该复制构造函数会存在风险,具体原因请查询有关 “浅拷贝” 、“深拷贝”的文章论述
32     Complex(const Complex & c)
33     {
34         // 将对象c中的数据成员值复制过来
35         m_real = c.m_real;
36         m_img  = c.m_img;
37     }
38
39     // 类型转换构造函数,根据一个指定的类型的对象创建一个本类的对象
40     // 例如:下面将根据一个double类型的对象创建了一个Complex对象
41     Complex::Complex(double r)
42     {
43         m_real = r;
44         m_imag = 0.0;
45     }
46
47     // 等号运算符重载
48     // 注意,这个类似复制构造函数,将=右边的本类对象的值复制给等号左边的对象,它不属于构造函数,等号左右两边的对象必须已经被创建
49     // 若没有显示的写=运算符重载,则系统也会创建一个默认的=运算符重载,只做一些基本的拷贝工作
50     Complex &operator=(const Complex &rhs)
51     {
52         // 首先检测等号右边的是否就是左边的对象本,若是本对象本身,则直接返回
53         if ( this == &rhs )
54         {
55             return *this;
56         }
57
58         // 复制等号右边的成员到左边的对象中
59         this->m_real = rhs.m_real;
60         this->m_imag = rhs.m_imag;
61
62         // 把等号左边的对象再次传出
63         // 目的是为了支持连等 eg:    a=b=c 系统首先运行 b=c
64         // 然后运行 a= ( b=c的返回值,这里应该是复制c值后的b对象)
65         return *this;
66     }
67 };

下面使用上面定义的类对象来说明各个构造函数的用法:

 1 void main()
 2 {
 3     // 调用了无参构造函数,数据成员初值被赋为0.0
 4     Complex c1,c2;
 5
 6     // 调用一般构造函数,数据成员初值被赋为指定值
 7     Complex c3(1.0,2.5);
 8     // 也可以使用下面的形式
 9     Complex c3 = Complex(1.0,2.5);
10
11     // 把c3的数据成员的值赋值给c1
12     // 由于c1已经事先被创建,故此处不会调用任何构造函数
13     // 只会调用 = 号运算符重载函数
14     c1 = c3;
15
16     // 调用类型转换构造函数
17     // 系统首先调用类型转换构造函数,将5.2创建为一个本类的临时对象,然后调用等号运算符重载,将该临时对象赋值给c1
18     c2 = 5.2;
19
20     // 调用拷贝构造函数( 有下面两种调用方式)
21     Complex c5(c2);
22     Complex c4 = c2;  // 注意和 = 运算符重载区分,这里等号左边的对象不是事先已经创建,故需要调用拷贝构造函数,参数为c2
23
24 }

参考:http://www.cnblogs.com/xkfz007/archive/2012/05/11/2496447.html

复制构造函数

几个原则:

C++ primer p406 :复制构造函数是一种特殊的构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显示使用复制构造函数。当该类型的对象传递给函数或从函数返回该类型的对象时,将隐式调用复制构造函数。

C++支持两种初始化形式:复制初始化(int a = 5;)和直接初始化(int a(5);)对于其他类型没有什么区别,对于类类型直接初始化直接调用实参匹配的构造函数,复制初始化总是调用复制构造函数,也就是说:

A x(2);  //直接初始化,调用构造函数
A y = x;  //复制初始化,调用复制构造函数

必须定义复制构造函数的情况:

只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义复制构造函数也可以复制;有的类有一个数据成员是指针,或者是有成员表示在构造函数中分配的其他资源,这两种情况下都必须定义复制构造函数。

什么情况使用复制构造函数:

类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
(1)一个对象以值传递的方式传入函数体 
(2)一个对象以值传递的方式从函数返回 
(3)一个对象需要通过另外一个对象进行初始化。

深拷贝和浅拷贝:

所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间

如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝

上面提到,如果没有自定义复制构造函数,则系统会创建默认的复制构造函数,但系统创建的默认复制构造函数只会执行“浅拷贝”,即将被拷贝对象的数据成员的值一一赋值给新创建的对象,若该类的数据成员中有指针成员,则会使得新的对象的指针所指向的地址与被拷贝对象的指针所指向的地址相同,delete该指针时则会导致两次重复delete而出错。下面是示例:

 1 #include <iostream.h>
 2 #include <string.h>
 3 class Person
 4 {
 5 public :
 6
 7     // 构造函数
 8     Person(char * pN)
 9     {
10         cout << "一般构造函数被调用 !\n";
11         m_pName = new char[strlen(pN) + 1];
12         //在堆中开辟一个内存块存放pN所指的字符串
13         if(m_pName != NULL)
14         {
15            //如果m_pName不是空指针,则把形参指针pN所指的字符串复制给它
16              strcpy(m_pName ,pN);
17         }
18     }
19
20     // 系统创建的默认复制构造函数,只做位模式拷贝
21     Person(Person & p)
22     {
23         //使两个字符串指针指向同一地址位置
24         m_pName = p.m_pName;
25     }
26
27     ~Person( )
28     {
29         delete m_pName;
30     }
31
32 private :
33     char * m_pName;
34 };
35
36 void main( )
37 {
38     Person man("lujun");
39     Person woman(man);
40
41     // 结果导致   man 和    woman 的指针都指向了同一个地址
42
43     // 函数结束析构时
44     // 同一个地址被delete两次
45 }
46
47
48 // 下面自己设计复制构造函数,实现“深拷贝”,即不让指针指向同一地址,而是重新申请一块内存给新的对象的指针数据成员
49 Person(Person & chs);
50 {
51      // 用运算符new为新对象的指针数据成员分配空间
52      m_pName=new char[strlen(p.m_pName)+ 1];
53
54      if(m_pName)
55      {
56              // 复制内容
57             strcpy(m_pName ,chs.m_pName);
58      }
59
60     // 则新创建的对象的m_pName与原对象chs的m_pName不再指向同一地址了
61 }

重载赋值操作符:

通过定义operate=的函数,可以对赋值进行定义。像其他任何函数一样,操作符函数有一个返回值和形参表。形参表必须具有与该操作符操作数书目相同的形参(如果操作符是一个成员,则包括隐式this形参)。赋值是二元运算,所以该操作符函数有两个形参:第一个形参(隐含的this指针)对应着左操作数,第二个形参对应右操作数。

一个应用了对赋值号重载的拷贝构造函数的例子:

 1 #include <iostream>
 2
 3 using namespace std;
 4
 5 class A
 6 {
 7 public:
 8     A(int);//构造函数
 9     A(const A &);//拷贝构造函数
10     ~A();
11     void print();
12     int *point;
13     A &operator=(const A &);
14 };
15
16 A::A(int p)
17 {
18     point = new int;
19     *point = p;
20 }
21
22 A::A(const A &b)
23 {
24     *this = b;
25     cout<<"调用拷贝构造函数"<<endl;
26 }
27
28 A::~A()
29 {
30     delete point;
31 }
32
33 void A::print()
34 {
35     cout<<"Address:"<<point<<" value:"<<*point<<endl;
36 }
37
38 A &A::operator=(const A &b)
39 {
40     if( this != &b)
41     {
42         delete point;
43         point = new int;
44         *point = *b.point;
45     }
46 }
47
48
49 int main()
50 {
51     A x(2);
52     A y = x;
53     x.print();
54     delete x.point;
55     y.print();
56
57     return 0;
58 }

参见:C++拷贝构造函数详解:http://blog.csdn.net/lwbeyond/article/details/6202256

理解C++中拷贝构造函数相关推荐

  1. C++中拷贝构造函数

    C++中拷贝构造函数 拷贝构造函数根据名字可以看得出来,当对象发生拷贝时调用的函数.该函数与构造函数和析构函数一样,不会被显式调用.我们只是说当对象发生拷贝时调用,那么什么时候会发生对象拷贝呢?我们来 ...

  2. c++中拷贝构造函数被调用的时机

    1 c++中拷贝构造函数被调用的时机 拷贝构造函数被调用的几种情况: (1)当用类的一个对象去初始化该类的另一个对象时,系统会自动调用拷贝构造函数: (2)将一个对象作为实参传递给一个非引用类型的形参 ...

  3. c++中拷贝构造函数、默认无参构造函数、析构函数的理解

    1 struct Exmpl 2 { 3 //无参默认构造函数 4 Exmpl(){cout<<"Exmpl()"<<endl;} 5 //拷贝构造函数 6 ...

  4. C++中拷贝构造函数与赋值构造函数详解

    1.  拷贝构造函数 什么时候会调用拷贝构造函数  ?            使用一个已经创建好的对象来初始化一个新的对象. Student mya("zhang3",22);   ...

  5. 深入探索C++对象模型一书中拷贝构造函数和NRV关系探讨

    转自:http://blog.guorongfei.com/2016/01/11/cpp-copy-constructor-nrv/ 最近深入探索C++对象模型一书,对于P67中最后一段话的第一句非常 ...

  6. 【深入理解C++】拷贝构造函数

    文章目录 1.拷贝构造函数 2.默认的拷贝操作 3.默认拷贝构造函数 4.何时调用拷贝构造函数 1.拷贝构造函数 拷贝构造函数是构造函数的一种.当利用已存在的对象创建一个新对象时,就会调用新对象的拷贝 ...

  7. C++中拷贝构造函数的形参为什么要是const引用

    1.首先为什么要是引用: 如对于 CBox(CBox initB);//复制构造函数一开始想到的原型 CBox cigar; CBox myBox(cigar); //如果编写这样一条语句 //那么将 ...

  8. 备忘录_C++_拷贝构造函数

    脚踏实地,仰望星空 目录视图 摘要视图 订阅 程序员必须要学会算法吗     博客专家庄晓立:我为什么要选择Rust?     从零练就iOS高手实战班震撼来袭     新型数据库利弊谈     C+ ...

  9. 继承的时候,子类的拷贝构造函数和重载运算符的实现

    由[1] [2] 例子看出:子类中实现了拷贝构造和赋值函数,则调用子类的拷贝构造和赋值函数 [1]说明: 子类的拷贝构造中调用父类的拷贝构造: 子类的赋值函数中调用父类的赋值函数 #include&l ...

最新文章

  1. hexo博客更新主题后上传Git操作
  2. Hyper-V 2016 系列教程15 Hyper-V Cmdlets PowerShell 命令提示符
  3. Linux内核--网络协议栈深入分析(二)--sk_buff的操作函数
  4. 计算机网络可被理解为( )
  5. Active Directory系列之十七:实战详解域信任关系
  6. 二分查找算法举例说明C语言,C语言快速排序与二分查找算法示例
  7. 恢复Linux系统权限
  8. lvs-nat负载均衡实验
  9. canva怎么拼接图片_小间距LED显示屏怎么拼接成2K,4K和8K显示屏_小间距显示屏厂家为您科普...
  10. 计算机设计大赛作品评语,第九届大学生短片大赛获奖作品评语
  11. 三维重建-立体校正(Recitification)
  12. mysql mapper配置模糊查询_mybatis模糊查询、分页和别名配置的方法
  13. 怎样成为时间管理的高手
  14. 程序员常用的代码编辑器
  15. JavaScript提升(你不知道的JavaScript)
  16. 《Java从小白到大牛》第29章:项目实战1:开发PetStore宠物商店项目
  17. SparkStreaming-相关窗口操作
  18. ⭐Python轻松下载整理或删除微信收藏⚡
  19. 第十八届全国大学生智能汽车竞赛 讯飞-智慧农业挑战赛 全国选拔赛规则
  20. 细数汽车史上那些经典的发动机,你知道几个?

热门文章

  1. Weekly Contest 141
  2. 用jenkins创建节点
  3. 设计模式11---组合模式(Composite Pattern)
  4. 《Effective C++》阅后心得
  5. 淘宝开发平台知识点一,入门指南
  6. STL之deque和其他容器
  7. C++之泛型编程(模板)
  8. NumPy常用属性及方法
  9. C#递归搜索指定目录下的文件或目录
  10. css3 定义选择器