脚踏实地,仰望星空

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

C++拷贝构造函数详解

分类: C/C++2011-02-23 13:39 90869人阅读 评论(145) 收藏 举报
c++funclass编译器deletec

一. 什么是拷贝构造函数

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

[c-sharp] view plaincopy
  1. int a = 100;
  2. int b = a;

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

[c-sharp] view plaincopy
  1. #include <iostream>
  2. using namespace std;
  3. class CExample {
  4. private:
  5.  int a;
  6. public:
  7. //构造函数
  8.  CExample(int b)
  9.  { a = b;}
  10. //一般函数
  11.  void Show ()
  12.  {
  13. cout<<a<<endl;
  14. }
  15. };
  16. int main()
  17. {
  18.  CExample A(100);
  19.  CExample B = A; //注意这里的对象初始化要调用拷贝构造函数,而非赋值
  20.   B.Show ();
  21.  return 0;
  22. }

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

下面举例说明拷贝构造函数的工作过程。

[c-sharp] view plaincopy
  1. #include <iostream>
  2. using namespace std;
  3. class CExample {
  4. private:
  5. int a;
  6. public:
  7. //构造函数
  8. CExample(int b)
  9. { a = b;}
  10. //拷贝构造函数
  11. CExample(const CExample& C)
  12. {
  13. a = C.a;
  14. }
  15. //一般函数
  16. void Show ()
  17. {
  18. cout<<a<<endl;
  19. }
  20. };
  21. int main()
  22. {
  23. CExample A(100);
  24. CExample B = A; // CExample B(A); 也是一样的
  25. B.Show ();
  26. return 0;
  27. }

CExample(const CExample& C) 就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量

二. 拷贝构造函数的调用时机

在C++中,下面三种对象需要调用拷贝构造函数!
1. 对象以值传递的方式传入函数参数

[c-sharp] view plaincopy
  1. class CExample   
  2. {  
  3. private:  
  4.  int a;  
  5.   
  6. public:  
  7.  //构造函数  
  8.  CExample(int b)  
  9.  {   
  10.   a = b;  
  11.   cout<<"creat: "<<a<<endl;  
  12.  }  
  13.   
  14.  //拷贝构造  
  15.  CExample(const CExample& C)  
  16.  {  
  17.   a = C.a;  
  18.   cout<<"copy"<<endl;  
  19.  }  
  20.    
  21.  //析构函数  
  22.  ~CExample()  
  23.  {  
  24.   cout<< "delete: "<<a<<endl;  
  25.  }  
  26.   
  27.      void Show ()  
  28.  {  
  29.          cout<<a<<endl;  
  30.      }  
  31. };  
  32.   
  33. //全局函数,传入的是对象  
  34. void g_Fun(CExample C)  
  35. {  
  36.  cout<<"test"<<endl;  
  37. }  
  38.   
  39. int main()  
  40. {  
  41.  CExample test(1);  
  42.  //传入对象  
  43.  g_Fun(test);  
  44.   
  45.  return 0;  
  46. }  


调用g_Fun()时,会产生以下几个重要步骤:
(1).test对象传入形参时,会先会产生一个临时变量,就叫 C 吧。
(2).然后调用拷贝构造函数把test的值给C。 整个这两个步骤有点像:CExample C(test);
(3).等g_Fun()执行完后, 析构掉 C 对象。

2. 对象以值传递的方式从函数返回

[c-sharp] view plaincopy
  1. class CExample   
  2. {  
  3. private:  
  4.  int a;  
  5.   
  6. public:  
  7.  //构造函数  
  8.  CExample(int b)  
  9.  {   
  10.   a = b;  
  11.  }  
  12.   
  13.  //拷贝构造  
  14.  CExample(const CExample& C)  
  15.  {  
  16.   a = C.a;  
  17.   cout<<"copy"<<endl;  
  18.  }  
  19.   
  20.      void Show ()  
  21.      {  
  22.          cout<<a<<endl;  
  23.      }  
  24. };  
  25.   
  26. //全局函数  
  27. CExample g_Fun()  
  28. {  
  29.  CExample temp(0);  
  30.  return temp;  
  31. }  
  32.   
  33. int main()  
  34. {  
  35.  g_Fun();  
  36.  return 0;  
  37. }  


当g_Fun()函数执行到return时,会产生以下几个重要步骤:
(1). 先会产生一个临时变量,就叫XXXX吧。
(2). 然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);
(3). 在函数执行到最后先析构temp局部变量。
(4). 等g_Fun()执行完后再析构掉XXXX对象。

3. 对象需要通过另外一个对象进行初始化;

[c-sharp] view plaincopy
  1. CExample A(100);  
  2. CExample B = A;   
  3. // CExample B(A);   

后两句都会调用拷贝构造函数。

三. 浅拷贝和深拷贝

1. 默认拷贝构造函数

很多时候在我们都不知道拷贝构造函数的情况下,传递对象给函数参数或者函数返回对象都能很好的进行,这是因为编译器会给我们自动产生一个拷贝构造函数,这就是“默认拷贝构造函数”,这个构造函数很简单,仅仅使用“老对象”的数据成员的值对“新对象”的数据成员一一进行赋值,它一般具有以下形式:

[c-sharp] view plaincopy
  1. Rect::Rect(const Rect& r)
  2. {
  3. width = r.width;
  4. height = r.height;
  5. }

当然,以上代码不用我们编写,编译器会为我们自动生成。但是如果认为这样就可以解决对象的复制问题,那就错了,让我们来考虑以下一段代码:

[c-sharp] view plaincopy
  1. class Rect
  2. {
  3. public:
  4. Rect()      // 构造函数,计数器加1
  5. {
  6. count++;
  7. }
  8. ~Rect()     // 析构函数,计数器减1
  9. {
  10. count--;
  11. }
  12. static int getCount()       // 返回计数器的值
  13. {
  14. return count;
  15. }
  16. private:
  17. int width;
  18. int height;
  19. static int count;       // 一静态成员做为计数器
  20. };
  21. int Rect::count = 0;        // 初始化计数器
  22. int main()
  23. {
  24. Rect rect1;
  25. cout<<"The count of Rect: "<<Rect::getCount()<<endl;
  26. Rect rect2(rect1);   // 使用rect1复制rect2,此时应该有两个对象
  27. cout<<"The count of Rect: "<<Rect::getCount()<<endl;
  28. return 0;
  29. }

  这段代码对前面的类,加入了一个静态成员,目的是进行计数。在主函数中,首先创建对象rect1,输出此时的对象个数,然后使用rect1复制出对象rect2,再输出此时的对象个数,按照理解,此时应该有两个对象存在,但实际程序运行时,输出的都是1,反应出只有1个对象。此外,在销毁对象时,由于会调用销毁两个对象,类的析构函数会调用两次,此时的计数器将变为负数。

说白了,就是拷贝构造函数没有处理静态数据成员。

出现这些问题最根本就在于在复制对象时,计数器没有递增,我们重新编写拷贝构造函数,如下:

[c-sharp] view plaincopy
  1. class Rect
  2. {
  3. public:
  4. Rect()      // 构造函数,计数器加1
  5. {
  6. count++;
  7. }
  8. Rect(const Rect& r)   // 拷贝构造函数
  9. {
  10. width = r.width;
  11. height = r.height;
  12. count++;          // 计数器加1
  13. }
  14. ~Rect()     // 析构函数,计数器减1
  15. {
  16. count--;
  17. }
  18. static int getCount()   // 返回计数器的值
  19. {
  20. return count;
  21. }
  22. private:
  23. int width;
  24. int height;
  25. static int count;       // 一静态成员做为计数器
  26. };

2. 浅拷贝

所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。大多情况下“浅拷贝”已经能很好地工作了,但是一旦对象存在了动态成员,那么浅拷贝就会出问题了,让我们考虑如下一段代码:

[c-sharp] view plaincopy
  1. class Rect
  2. {
  3. public:
  4. Rect()      // 构造函数,p指向堆中分配的一空间
  5. {
  6. p = new int(100);
  7. }
  8. ~Rect()     // 析构函数,释放动态分配的空间
  9. {
  10. if(p != NULL)
  11. {
  12. delete p;
  13. }
  14. }
  15. private:
  16. int width;
  17. int height;
  18. int *p;     // 一指针成员
  19. };
  20. int main()
  21. {
  22. Rect rect1;
  23. Rect rect2(rect1);   // 复制对象
  24. return 0;
  25. }

在这段代码运行结束之前,会出现一个运行错误。原因就在于在进行对象复制时,对于动态分配的内容没有进行正确的操作。我们来分析一下:

在运行定义rect1对象后,由于在构造函数中有一个动态分配的语句,因此执行后的内存情况大致如下:

在使用rect1复制rect2时,由于执行的是浅拷贝,只是将成员的值进行赋值,这时 rect1.p = rect2.p,也即这两个指针指向了堆里的同一个空间,如下图所示:

当然,这不是我们所期望的结果,在销毁对象时,两个对象的析构函数将对同一个内存空间释放两次,这就是错误出现的原因。我们需要的不是两个p有相同的值,而是两个p指向的空间有相同的值,解决办法就是使用“深拷贝”。

3. 深拷贝

在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间,如上面的例子就应该按照如下的方式进行处理:

[c-sharp] view plaincopy
  1. class Rect
  2. {
  3. public:
  4. Rect()      // 构造函数,p指向堆中分配的一空间
  5. {
  6. p = new int(100);
  7. }
  8. Rect(const Rect& r)
  9. {
  10. width = r.width;
  11. height = r.height;
  12. p = new int;    // 为新对象重新动态分配空间
  13. *p = *(r.p);
  14. }
  15. ~Rect()     // 析构函数,释放动态分配的空间
  16. {
  17. if(p != NULL)
  18. {
  19. delete p;
  20. }
  21. }
  22. private:
  23. int width;
  24. int height;
  25. int *p;     // 一指针成员
  26. };

此时,在完成对象的复制后,内存的一个大致情况如下:

此时rect1的p和rect2的p各自指向一段内存空间,但它们指向的空间具有相同的内容,这就是所谓的“深拷贝”。

3. 防止默认拷贝发生

通过对对象复制的分析,我们发现对象的复制大多在进行“值传递”时发生,这里有一个小技巧可以防止按值传递——声明一个私有拷贝构造函数。甚至不必去定义这个拷贝构造函数,这样因为拷贝构造函数是私有的,如果用户试图按值传递或函数返回该类对象,将得到一个编译错误,从而可以避免按值传递或返回对象。

[c-sharp] view plaincopy
  1. // 防止按值传递
  2. class CExample
  3. {
  4. private:
  5. int a;
  6. public:
  7. //构造函数
  8. CExample(int b)
  9. {
  10. a = b;
  11. cout<<"creat: "<<a<<endl;
  12. }
  13. private:
  14. //拷贝构造,只是声明
  15. CExample(const CExample& C);
  16. public:
  17. ~CExample()
  18. {
  19. cout<< "delete: "<<a<<endl;
  20. }
  21. void Show ()
  22. {
  23. cout<<a<<endl;
  24. }
  25. };
  26. //全局函数
  27. void g_Fun(CExample C)
  28. {
  29. cout<<"test"<<endl;
  30. }
  31. int main()
  32. {
  33. CExample test(1);
  34. //g_Fun(test); 按值传递将出错
  35. return 0;
  36. }

四. 拷贝构造函数的几个细节

1. 拷贝构造函数里能调用private成员变量吗?
解答:
这个问题是在网上见的,当时一下子有点晕。其时从名子我们就知道拷贝构造函数其时就是一个特殊的构造函数,操作的还是自己类的成员变量,所以不受private的限制。

2. 以下函数哪个是拷贝构造函数,为什么?

[c-sharp] view plaincopy
  1. X::X(const X&);
  2. X::X(X);
  3. X::X(X&, int a=1);
  4. X::X(X&, int a=1, int b=2);


解答:对于一个类X, 如果一个构造函数的第一个参数是下列之一:
a) X&
b) const X&
c) volatile X&
d) const volatile X&
且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数.

[c-sharp] view plaincopy
  1. X::X(const X&);  //是拷贝构造函数
  2. X::X(X&, int=1); //是拷贝构造函数
  3. X::X(X&, int a=1, int b=2); //当然也是拷贝构造函数

3. 一个类中可以存在多于一个的拷贝构造函数吗?
解答:
类中可以存在超过一个拷贝构造函数。

[c-sharp] view plaincopy
  1. class X {
  2. public:
  3. X(const X&);      // const 的拷贝构造
  4. X(X&);            // 非const的拷贝构造
  5. };

注意,如果一个类中只存在一个参数为 X& 的拷贝构造函数,那么就不能使用const X或volatile X的对象实行拷贝初始化.

[c-sharp] view plaincopy
  1. class X {
  2. public:
  3. X();
  4. X(X&);
  5. };
  6. const X cx;
  7. X x = cx;    // error

如果一个类中没有定义拷贝构造函数,那么编译器会自动产生一个默认的拷贝构造函数。
这个默认的参数可能为 X::X(const X&)或 X::X(X&),由编译器根据上下文决定选择哪一个。

版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 上一篇c/c++中typedef详解
  • 下一篇C++类型转换详解--const_cast
主题推荐
对象结构类classcolor
猜你在找
数据结构和算法
C语言及程序设计初步
Part 1:基础语言-Cocos2d-x手机游戏开发必备C++语言基础
Hadoop 2.X企业级开发入门系列
深入浅出MySQL入门必备
Stringc++详解
VC++常用数据类型及其操作详解非常经典共同分享
VC++常用数据类型及其操作详解非常经典共同分享
VC++常用数据类型及其操作详解
VC++常用数据类型及其操作详解
准备好了么? 跳吧             !更多职位尽在 CSDN JOB
windows C/C++开发工程师
北京博睿宏远科技发展有限公司

|

6-10K/月

我要跳槽

C++开发工程师
上海晶赞科技发展有限公司

|

12-24K/月

我要跳槽

美国C++软件工程师
北京玛赫西计算机教育咨询有限公司

|

40-80K/月

我要跳槽

linux C/C++开发
中国科学院通用芯片与基础软件研究中心

|

15-20K/月

我要跳槽

id="ad_frm_0" frameborder="0" scrolling="no" src="http://blog.csdn.net/common/ad.html?t=4&containerId=ad_cen&frmId=ad_frm_0" style="border-width: 0px; overflow: hidden; width: 746px; height: 90px;">
查看评论
123楼 iamtyk 2015-06-21 08:13发表 [回复]
楼主
X::X(const X&)或 X::X(X&),
X&是什么参数
122楼 iamtyk 2015-06-21 08:12发表 [回复]
X::X(const X&)或 X::X(X&),
楼主..X&是什么参数
121楼 zhengjingsen 2015-06-03 11:12发表 [回复]
讲的特别详细,让我搞清楚了一个问题
120楼 yinglinghuang888888 2015-06-03 10:54发表 [回复]
深度了解了复制构造函数,感谢楼主。
119楼 BGY_Bluesky 2015-05-28 15:32发表 [回复]
非常好的帖子,仔细阅读并跟着上机实践,真是受益匪浅,感谢分享,终于深刻理解类的拷贝构造函数了,赞赞 赞
118楼 SmilingSunrise 2015-05-13 16:36发表 [回复]
不错,写的很详细具体!
117楼 veryitman 2015-05-09 22:10发表 [回复]
辛苦楼主了.赞一个!
不过你的例子应该是在 vs 上面验证的,在 mac 平台下 xcode 编译运行有点不一样.
116楼 Felix_0920 2015-05-08 17:30发表 [回复]
请教楼主一个问题,对于用户自定义的类,如果没有默认的无参构造函数,那么编译器是如果生成类的临时变量对象的?
115楼 sinat_27624023 2015-04-22 16:36发表 [回复]
博主,想请教你一个问题。拷贝构造函数能将一个对象的string类字符串拷贝给另一个对象吗?
114楼 CourageK 2015-04-02 09:35发表 [回复]
楼主真是太有心了,让好多人都对复制构造函数有了很深入的理解,从此面试此类问题不再纠结了。谢谢你~
113楼 编程艺术家 2015-03-29 16:00发表 [回复]
mark
112楼 qszchew 2015-03-17 19:36发表 [回复]
讲得真好!以前一直搞不懂的!
111楼 li1191863273 2015-02-11 03:04发表 [回复]
受教了
110楼 mz490848173 2015-02-09 13:45发表 [回复]
想问一下 那个 复制构造函数 所复制的函数 是在 堆内存里 吗 还是 栈内存里
109楼 小米mimica 2015-01-15 20:21发表 [回复]
写的非常详细,也很易于理解!受益匪浅!
108楼 jinjinwu 2015-01-14 15:45发表 [回复]
看书一直没看懂,博主讲的很清楚啊,
107楼 Sungyers 2015-01-10 15:54发表 [回复]
博主给力,狂赞!
106楼 louObaichu 2015-01-06 15:28发表 [回复]
mark
105楼 ColaWJY 2014-12-16 21:54发表 [回复]
给力!Mark了!
104楼 Huai-yu 2014-12-04 09:05发表 [回复]
支持楼主,赞一个
103楼 御弟叔叔 2014-11-29 14:13发表 [回复]
赞一个!
102楼 _LebronLu_ 2014-11-28 11:16发表 [回复]
很详细,佩服!
101楼 jyf823691221 2014-11-13 17:00发表 [回复]
赞一个 写的太好了 豁然开朗呀
100楼 eyeshot_yang33 2014-11-06 10:16发表 [回复]
谢谢,看完豁然开朗了!
99楼 hityxhvp 2014-11-05 21:48发表 [回复]
通读全文,非常感谢!
98楼 tandyx 2014-11-03 17:10发表 [回复]
很不错, 非常给力。 特地登陆来点赞。 我的密码都试了好多次才登上来。 哈哈。
97楼 zhaolianyun 2014-10-26 20:28发表 [回复]
真心不错,非常受教!!!
96楼 wjwizard 2014-10-23 19:05发表 [回复]
赞一个,好贴
95楼 eternal_tune 2014-10-14 12:41发表 [回复]
CExample B = A; // CExample B(A); 也是一样的 
这段代码应该是赋值构造函数(操作符为‘=’)。如果将此代码放在private里面就编译不过:
CExample & operator=(const CExample&);

CExample B(A);此段才是调用的拷贝构造函数

Re: wobushixiaomi 2014-12-31 14:31发表 [回复]
回复u012844596:正解!
94楼 Sylvernass 2014-10-13 19:21发表 [回复]
写的真心很不错,这部分对初学者来说会搞的有点混,但是看了这篇文章,顿时明白了很多,很清晰,不得不来留言点赞
93楼 royliu1 2014-10-11 12:54发表 [回复]
写得好! 顺便看了下前辈们的讨论,有收获! 谢谢
92楼 Edwin404 2014-09-30 16:33发表 [回复]
问个问题,为啥“2. 对象以值传递的方式从函数返回”这个标题下面那个例子什么都没有打印呢?
91楼 lichangyu2011 2014-09-29 10:05发表 [回复]
转载了!学知识真应该这样,受教了!惭愧一个!!!
90楼 puppylpg 2014-09-18 20:09发表 [回复]
太详细了!果断赞起!
89楼 terry_o0o 2014-09-17 16:37发表 [回复]
一直不太理解,就找到lz这个文章了,豁然开朗!
88楼 jiaqingmin1990 2014-09-15 10:50发表 [回复]
nice....
87楼 wangxm1111 2014-09-14 16:49发表 [回复]
一下子思路很清晰,讲的很不错
86楼 图像配准菜鸟 2014-09-13 10:36发表 [回复]
受教了!谢谢你耐心的讲解!
85楼 他们叫我周周周 2014-09-12 14:09发表 [回复]
太棒了~
84楼 忆之独秀 2014-09-06 16:16发表 [回复]
写的不能更好
83楼 贪睡的萝卜 2014-09-05 16:43发表 [回复]
受益匪浅,感谢博主
82楼 叶子399 2014-09-02 11:03发表 [回复]
大赞~
81楼 旭梦1990 2014-08-25 08:44发表 [回复]
太好了,了解了拷贝构造函数
80楼 uiong8163 2014-08-09 00:38发表 [回复]
大神,很感谢!
79楼 panjunnn 2014-08-07 14:47发表 [回复]
受教了,好啊
78楼 坚决不做程序狗 2014-08-07 08:48发表 [回复]
牛逼。。。。学习了
77楼 PushFang 2014-07-30 22:36发表 [回复]
大赞!!!!
76楼 笃志近思 2014-07-25 17:16发表 [回复]
受教了,写的非常棒!
75楼 bin03 2014-07-21 11:30发表 [回复]
很好。条理清晰。
74楼 某种意境 2014-07-09 23:16发表 [回复]
终于晓得这玩意儿了
73楼 junjie58msp 2014-07-02 11:08发表 [回复]
此文写的太好了。赞一个。
72楼 HUASHUIXIAOHAI 2014-06-27 14:49发表 [回复]
楼主写的真心不错,大赞!
71楼 xanarry 2014-06-19 09:12发表 [回复]
受益不浅,谢谢博主
70楼 卿笃军 2014-05-25 08:47发表 [回复]
拷贝构造函数:
A(A a){}
值传递传参为什么会编译通不过?

Re: heipacker 2014-06-04 23:11发表 [回复]
回复u012339743:这个不是复制构造函数

Re: 卿笃军 2014-06-05 22:18发表 [回复]
回复fupacker:我只是想问这种传参方式 编译为何通不过。

Re: heipacker 2014-06-06 23:11发表 [回复]
回复u012339743:编译不通过是因为A a这里的定义问题,只有在看到一个类的定义的时候才能把一个数据成员声明为这个类型的对象,
但是当一个类的类头被看到时,它就被视为已经被声明了,所以一个类可以用指向自身类型的指针或引用作为数据成员

这里数据成员你也可以认为是一个变量的定义 看看C++ primer

Re: 卿笃军 2014-06-07 00:25发表 [回复]
回复fupacker:这个我明白。就像结构体一样:
struct node 
{
struct node next; //错误
};
struct node 
{
struct node *next; //正确
};
————————————
但是:
class A
{
public:
A(A a); //错误
void Print(A a){};//正确
};
这个是如何解释?

Re: gdouse1093 2014-09-28 22:02发表 [回复]
回复u012339743:如果不是引用会造成无限递归, (会无限的调用拷贝构造函数)
Re: heipacker 2014-06-07 13:45发表 [回复]
回复u012339743:拷贝构造函数一定要使用引用传递 你查一下就知道了

Re: 卿笃军 2014-06-07 14:55发表 [回复]
回复fupacker:这个我查过,也明白A(A a)这样会带来的问题。
就是上面那个问题有些不明白。

Re: 小王王小 2014-08-04 16:37发表 [回复]
回复u012339743:拷贝构造函数如果允许值传递,那么实参对象传递给形参对象,会拥有两个存储空间,新创建的临时存储空间对象初始化时,同样需要调用拷贝构造函数,以此类推,会永无休止的调用拷贝构造函数,最终会导致栈溢出。

Re: 卿笃军 2014-09-28 23:42发表 [回复]
回复wangyong19881123:我明白了。
69楼 动感蚂蚁 2014-05-15 13:03发表 [回复]
学习了,谢谢
68楼 tian15134549098 2014-04-21 20:41发表 [回复]
超级给力,赞
67楼 lvjing2 2014-04-20 10:52发表 [回复]
必须赞一个!
66楼 吉米芽菜 2014-03-30 10:49发表 [回复]
衷心地赞一个
65楼 松木 2014-03-23 18:06发表 [回复]
楼主分析得太好了,让我对拷贝构造函数有了更深的认识,赞!
64楼 雨中奔跑 2014-02-17 11:55发表 [回复]
很详细nice
63楼 loe 2013-12-30 17:27发表 [回复]
mark!
62楼 feierfeierfly 2013-12-25 16:20发表 [回复]
受教了!谢谢楼主!
61楼 无之念 2013-12-23 17:13发表 [回复]
受教了
60楼 chuyuanjie123 2013-12-16 22:14发表 [回复]
很好。思路清晰
59楼 曾是土木人 2013-12-15 00:19发表 [回复]
总结的很好
58楼 lcjwxd 2013-12-05 15:39发表 [回复]
初学者也来赞一个,以后好好学习,天天向上!
57楼 zzqq0103 2013-12-04 17:20发表 [回复]
真的说的很好,受教了!!
56楼 苍原狮啸 2013-11-27 11:58发表 [回复]
后文的添加的内容很给力
55楼 HXD974287503 2013-11-21 16:17发表 [回复]
好好!
54楼 virginia-qing 2013-11-21 14:29发表 [回复]
good
53楼 zhw_CZ 2013-11-20 22:59发表 [回复]
写的真好,谢谢。
52楼 yulu27 2013-11-11 16:37发表 [回复]
写的真不错
51楼 tyy_ing 2013-11-10 20:58发表 [回复]
写的很棒 怒赞
50楼 code_feng 2013-11-05 15:29发表 [回复]
学习了……谢谢楼主~
49楼 黑白尤文 2013-08-31 09:16发表 [回复]
请问文章中的图是用的什么工具画的啊?

Re: 黑白尤文 2013-09-14 22:34发表 [回复]
回复juvenfan:这么强大。。。 我用visio画出来的图都很丑……
Re: faith19871023 2013-09-07 15:34发表 [回复]
回复juvenfan:visio就可以啊
48楼 chz429 2013-08-25 16:06发表 [回复]
先赞一个~
关于对象以值传递的方式从函数返回会调用拷贝构造函数,怎么在Devc++测试的时候从函数返回不会调用拷贝构造函数,而在Visual Stdio却调用了呢

[cpp] view plaincopy
  1. #include<iostream>
  2. using namespace std;
  3. struct Node
  4. {
  5. int a, b;
  6. Node(int i, int j):a(i), b(j){}
  7. Node(const Node& a)
  8. {
  9. cout << "copy" << endl;
  10. }
  11. };
  12. Node fun()
  13. {
  14. Node n = Node(1,2);
  15. return n;
  16. }
  17. int main()
  18. {
  19. fun();
  20. system("pause");
  21. }
Re: whyisyoung 2014-11-25 23:05发表 [回复]
回复chz429:g++ 3.2 以上版本对编译器做了优化,导致第三种值返回的情况不调用拷贝构造函数, mac 上也不会调用, vs2012 仍调用,
47楼 lmnlkm 2013-08-22 16:18发表 [回复]
受教了,谢谢
46楼 鸭梨小乖宝宝 2013-08-15 18:35发表 [回复]
非常好的一篇文章,清晰又有条理
45楼 夏雪冬日 2013-08-10 14:48发表 [回复]
写的简单、清晰。还不错!
44楼 a429051366 2013-08-06 12:40发表 [回复]
真心希望楼主能写一本关于c++的书 ,业界的书可谓汗牛充栋,讲的明白清楚的寥若晨星,大部分的书都是讲讲语法,再贴上几个例子,看的人似懂非懂,云里雾里,整本书好像条理清楚,脉络清晰其实一塌糊涂,外国人的书又好点,可是看外文的大部头真的要耐心的,能说的这么清楚明白太难得了,真心希望楼主写本书 ,不写语法什么的 ,就和论坛里面的这种一样,真心的牛啊,要能给做几个c++的视频就更好了,赞一个
43楼 wsb929 2013-08-04 21:10发表 [回复]
顶 受教了 !! 谢谢!!!
42楼 五月初五 2013-08-03 11:53发表 [回复]
很好,受教了
41楼 云亦无心 2013-07-26 15:58发表 [回复]
讲的很有条理,分析透彻~
40楼 xiaominglang 2013-07-24 16:33发表 [回复]
受教了!!!楼主大牛!!!
39楼 __JacHan 2013-07-23 08:23发表 [回复]
学习啦,顶哥们,用的时候才能发现问题,有问题了再来跟哥们讨论
38楼 4unreal 2013-07-06 16:08发表 [回复]
很赞的文章
37楼 wennuan80 2013-06-25 09:24发表 [回复]
本人菜鸟,想问一下:
拷贝构造函数这样写行吗?
//拷贝构造函数
CExample(const CExample *C)
{
a = C->a;
}
36楼 wennuan80 2013-06-25 09:03发表 [回复]
为什么要用引用,指针不行吗?
35楼 MFCclassxiao 2013-06-18 10:25发表 [回复]
学习了,思路清晰了很多,谢谢大牛
34楼 wanhuatong1987 2013-06-14 10:02发表 [回复]
[cpp] view plaincopy
  1. Rect(const Rect& r)
  2. {
  3. width = r.width;
  4. height = r.height;
  5. p = new int;    // 为新对象重新动态分配空间
  6. *p = *(r.p);
  7. }

这样写在程序结束的时候调用了两次析构函数,不存在33楼说的内存泄露问题,这是用VS2010调试的结果。

33楼 MrSimp1e 2013-06-03 15:08发表 [回复]
深拷贝这里, 
Rect(const Rect& r) 

width = r.width; 
height = r.height; 
p = new int; // 为新对象重新动态分配空间 
*p = *(r.p); 
}

P重新分配了动态空间,但是原来的空间却没有释放掉。这样就造成了内存泄露吧。
Rect(const Rect& r) 

width = r.width; 
height = r.height; 
int *pTemp = p; // 将p指针指向的内存地址缓存起来
p = new int; // 为新对象重新动态分配空间 
*p = *(r.p); 
delete pTemp; // 释放p原来的内存

只是探讨,没有其他意思。

Re: Atlas 2013-06-18 09:28发表 [回复]
引用“bboyfeiyu”的评论:深拷贝这里, 
Rect(const Rect&amp; r) 

...
这样写的程序中,不会内存泄漏,但是另外一个问题,如果申请空间失败,但之前改变你实例中的部分,因为这里width = r.width; height = r.height; 已经执行了,是不是违反了异常安全性,这个怎么看?
Re: wanhuatong1987 2013-06-14 10:09发表 [回复]
回复bboyfeiyu:博友,你这样写的话会出现“ 0xC0000005: Access violation reading location 0xccccccc0.”错误,这是VS2010调试的结果。其实,文中那样写在退出程序的时候会调用两次析构函数,不会存在内存泄漏的问题。如有不对的地方,请指教。

Re: MrSimp1e 2013-06-14 12:46发表 [回复]
回复wanhuatong1987:gcc倒是不会报错,改成这样应该没有问题。
P重新分配了动态空间,但是原来的空间却没有释放掉。这样就造成了内存泄露吧。
Rect(const Rect& r) 

width = r.width; 
height = r.height; 
int *pTemp = p; // 将p指针指向的内存地址缓存起来,局部指针变量
p = new int; // 为新对象重新动态分配空间 
*p = *(r.p); 
}

Re: kkkwjx 2014-09-06 12:35发表 [回复]
回复bboyfeiyu:我认为不会出现内存泄露。
首先既然是构造函数,p最初就是一个野值,没有指向分配的内存,所以无需考虑释放原来的内存,直接进行内存分配即可。注意是直接分配不是重新分配。
反而觉得您的代码有问题,delete了一个野值,可能会出现问题。
32楼 LTheMiracle 2013-05-10 10:10发表 [回复]
挺有用的,谢谢!
31楼 蛋疼的 2013-04-22 15:20发表 [回复]
除非返回值的临时变量是在函数刚进入时先入栈。。

Re: NBHH0329 2014-07-25 16:57发表 [回复]
回复u010050719:从汇编上来说确实如此。临时变量XXX生成的汇编语句是更靠前的。
30楼 蛋疼的 2013-04-22 15:16发表 [回复]
(1). 先会产生一个临时变量,就叫XXXX吧。
(2). 然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);
(3). 在函数执行到最后先析构temp局部变量。
(4). 等g_Fun()执行完后再析构掉XXXX对象。
个人觉得先析构xxxx对象再析构temp局部变量
函数返回值如果大于8字节,就要创建一个临时内存来放临时变量,按照堆栈的先进后出原则,temp应该是先入栈,xxxx这个临时变量(其实只能叫做指针,指向一块存放该类型变量的内存)后入栈,这个指针指向的地址会存放在寄存器中,函数返回时寄存器EAX根据这个地址去找到临时变量的内容,你也许会说那函数返回,该函数的堆栈祯已经销毁,如何返回该临时变量的值,堆栈祯是被销毁了,但是程序不会清空其中的值,临时变量所在内存值依然存在。

Re: heipacker 2014-06-06 23:23发表 [回复]
回复u010050719:你自己分析的结论也是先析构的temp再析构XXX啊
Re: 江浙沪小团队移动外包 2013-10-11 15:34发表 [回复]
回复u010050719:本人亲测的确是先析构掉了函数内的局部变量,然后程序执行完后才析构函数返回时调用拷贝构造函数产生的临时对象。
Re: 夏雪冬日 2013-08-10 14:46发表 [回复]
回复u010050719:分析的到位。我的观点跟你一样!
29楼 蛋疼的 2013-04-22 15:03发表 [回复]
(1). 先会产生一个临时变量,就叫XXXX吧。
(2). 然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);
(3). 在函数执行到最后先析构temp局部变量。
(4). 等g_Fun()执行完后再析构掉XXXX对象。

第三和第四是不是顺序反了?先析构xxxx对象再析构temp对象

28楼 chaoguo1234 2013-04-18 16:26发表 [回复]
很好,不错
27楼 chm626905227 2013-04-12 17:28发表 [回复]
很好的一篇学习拷贝构造函数的文章,顶一个~
26楼 曦花 2013-04-08 13:50发表 [回复]
为什么要加const?
为什么要用引用?指针行吗?
25楼 hulaquandequan 2013-04-07 16:15发表 [回复]
真心不错~ 感谢~
24楼 hubi 2013-03-22 16:10发表 [回复]
棒!!!!!!!!!
23楼 jackielzjn 2013-03-14 14:42发表 [回复]
谢谢楼主,高手就是不一样。顶一个。。。。。。。
22楼 jouyouwen 2013-03-10 16:51发表 [回复]
写的很好,受教了,谢谢!
21楼 FranklinLING 2013-03-08 20:32发表 [回复]
哈哈,和其它评论有同感:简单、透彻!多谢!!
20楼 她大哥 2013-03-04 14:51发表 [回复]
写的太好了
19楼 waldobear 2013-03-02 18:14发表 [回复]
很清楚,收获不小。
18楼 na_nalove_huahua 2013-02-22 18:25发表 [回复]
文章写的很棒 解答了我很多疑惑
17楼 fly2sky 2013-02-21 23:11发表 [回复]
2中的问题又验证了一下,在vc下就会调用拷贝构造函数,在linux下用g++编译则不会调用拷贝构造函数,看来和编译器有关,文章很给力,学习了
16楼 fly2sky 2013-02-21 22:48发表 [回复]
2. 对象以值传递的方式从函数返回 这个给的例子验证了一下,并没有调用到拷贝构造函数,请博主试一下
15楼 Ritter_Liu 2013-02-19 20:03发表 [回复]
此文写的太好了
14楼 青茶柠檬 2013-01-30 14:36发表 [回复]
谢谢楼主,讲的很清楚。转走了
13楼 skkks 2013-01-20 10:21发表 [回复]
条理很清晰,喜欢这样的风格,谢谢~~
12楼 GrimRaider 2013-01-09 11:22发表 [回复]
I like it! Thank you!
11楼 windyrain999 2012-12-23 12:19发表 [回复]
谢谢您,对我很有帮助!!!
10楼 nashouat 2012-12-22 10:07发表 [回复]
太好了,讲得很通俗易懂,很容易理解,谢谢楼主,真的感谢!现在对拷贝构造函数清晰多了。谢谢!
9楼 _北方的雪_ 2012-12-19 11:05发表 [回复]
对(四)中第一个问题也是有疑问:在拷贝构造函数中,形参是对象的引用,在函数体中怎么可以直接用对象调用私有成员呢?

Re: 夏雪冬日 2013-08-10 14:43发表 [回复]
回复sgs1018:拷贝构造函数是一种特殊的构造函数。类的成员函数是可以访问该类的private成员的。
8楼 yintangzengli 2012-12-05 09:52发表 [回复]
写的蛮不错的,例子有对比,真的是让我看进去了的。
7楼 zhengtong0416 2012-11-17 11:40发表 [回复]
受教!向您学习
6楼 lwbeyond 2012-10-26 13:06发表 [回复]
共同进步啊
5楼 LinuxMan 2012-10-24 09:16发表 [回复]
很容易理解,赞一个
4楼 骑着乌龟去看海 2012-10-24 09:00发表 [回复]
MK
3楼 hanzhijun58 2012-10-15 20:25发表 [回复]
受教了
2楼 MyLend 2012-10-05 22:01发表 [回复]
牛啊。。。
1楼 aoshikang 2011-04-21 16:53发表 [回复]
哥们,你这篇文章真给力!受教了!谢谢。
发表评论
  • 用 户 名:
  • u011562836
  • 评论内容:
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

id="ad_frm_1" frameborder="0" scrolling="no" src="http://blog.csdn.net/common/ad.html?t=5&containerId=ad_bot&frmId=ad_frm_1" style="border-width: 0px; overflow: hidden; width: 746px; height: 0px;">
核心技术类目
全部主题 Hadoop AWS 移动游戏 Java Android iOS Swift 智能硬件 DockerOpenStack VPN Spark ERP IE10 Eclipse CRM JavaScript 数据库 Ubuntu NFCWAP jQuery BI HTML5 Spring Apache .NET API HTML SDK IIS FedoraXML LBS Unity Splashtop UML components Windows Mobile Rails QEMU KDECassandra CloudStack FTC coremail OPhone CouchBase 云计算 iOS6 RackspaceWeb App SpringSide Maemo Compuware 大数据 aptech Perl Tornado RubyHibernate ThinkPHP HBase Pure Solr Angular Cloud Foundry Redis ScalaDjango Bootstrap

  • 个人资料
  •  
    lwbeyond
     
     
    • 访问:329953次
    • 积分:4418
    • 等级: 
    • 排名:第2978名
    • 原创:165篇
    • 转载:29篇
    • 译文:1篇
    • 评论:221条
  • 文章搜索
  • 博客专栏
  • STL学习笔记

    文章:16篇

    阅读:17506

  • 文章分类
  • C/C++(44)
  • Linux Shell(4)
  • STL(17)
  • Linux(33)
  • vxWorsk(3)
  • ACE(4)
  • Eclipse(2)
  • English(3)
  • 设计模式(25)
  • 网络编程(14)
  • 汽车电子(4)
  • 加密算法(1)
  • 正则表达式(2)
  • 生活(2)
  • 思维改变生活(4)
  • 嵌入式linux开发(8)
  • QT(14)
  • uC/OS-II(6)
  • cocos2d(1)
  • Java(6)
  • 文章存档
    • 2015年03月(3)
    • 2015年02月(4)
    • 2014年11月(8)
    • 2014年10月(2)
    • 2013年12月(1)
    • 展开
  • 阅读排行
  • C++拷贝构造函数详解(90856)
  • 如何正确的关闭 MFC 线程(14773)
  • C++类型转换详解--const_cast(8325)
  • 学习C++该看什么书?(6062)
  • SPI 协议(4545)
  • 汽车诊断简介(4240)
  • GDB调试精粹(4040)
  • VxWorks学习笔记 -- 信号量(3971)
  • QT 调用外部程序(3740)
  • 学习 effortless English 20天了(3366)
  • 评论排行
  • C++拷贝构造函数详解(145)
  • 如何正确的关闭 MFC 线程(7)
  • C++类型转换详解--const_cast(6)
  • 详解C++ friend关键字(6)
  • VxWorks学习笔记 -- 信号量(3)
  • strcpy 详解(3)
  • STL编程轻松入门(3)
  • sizeof 精要(3)
  • 自定义 Base64 加密算法(2)
  • 线程的等待(2)
  • 推荐文章
  • id="ad_frm_2" frameborder="0" scrolling="no" src="http://blog.csdn.net/common/ad.html?t=12&containerId=ad_commend&frmId=ad_frm_2" style="border-width: 0px; overflow: hidden; width: 182px; height: 200px;">
  • 最新评论
  • C++拷贝构造函数详解

    iamtyk: 楼主X::X(const X&)或 X::X(X&),X&是什么参数

  • C++拷贝构造函数详解

    iamtyk: X::X(const X&)或 X::X(X&),楼主..X&是什么参数

  • GDB调试精粹

    xiaozhaoying: 你好,“四、设置与清除断点”clean那里应该写错了,应该改为clear

  • windows下安装cocos2d-x-2.2.5

    夏至北国: 请问下,基于c++的怎么打包成apk啊

  • C++拷贝构造函数详解

    zhengjingsen: 讲的特别详细,让我搞清楚了一个问题

  • C++拷贝构造函数详解

    yinglinghuang888888: 深度了解了复制构造函数,感谢楼主。

  • C++拷贝构造函数详解

    BGY_Bluesky: 非常好的帖子,仔细阅读并跟着上机实践,真是受益匪浅,感谢分享,终于深刻理解类的拷贝构造函数了,赞赞 ...

  • C++拷贝构造函数详解

    SmilingSunrise: 不错,写的很详细具体!

  • GDB调试精粹

    菜鸟实验室: 帝仰大学的?

  • C++拷贝构造函数详解

    veryitman: 辛苦楼主了.赞一个!不过你的例子应该是在 vs 上面验证的,在 mac 平台下 xcode 编译运行...

公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告|合作伙伴|论坛反馈
网站客服杂志客服微博客服webmaster@csdn.net400-600-2320|北京创新乐知信息技术有限公司 版权所有|江苏乐知网络技术有限公司 提供商务支持
京 ICP 证 070598 号|Copyright © 1999-2014, CSDN.NET, All Rights Reserved 

  src="http://zz.csdn.net/bin/logs.php" frameborder="0" width="0" height="0">

脚踏实地,仰望星空

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

C++拷贝构造函数详解

分类: C/C++2011-02-23 13:39 90869人阅读 评论(145) 收藏 举报
c++funclass编译器deletec

一. 什么是拷贝构造函数

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

[c-sharp] view plaincopy
  1. int a = 100;
  2. int b = a;

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

[c-sharp] view plaincopy
  1. #include <iostream>
  2. using namespace std;
  3. class CExample {
  4. private:
  5.  int a;
  6. public:
  7. //构造函数
  8.  CExample(int b)
  9.  { a = b;}
  10. //一般函数
  11.  void Show ()
  12.  {
  13. cout<<a<<endl;
  14. }
  15. };
  16. int main()
  17. {
  18.  CExample A(100);
  19.  CExample B = A; //注意这里的对象初始化要调用拷贝构造函数,而非赋值
  20.   B.Show ();
  21.  return 0;
  22. }

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

下面举例说明拷贝构造函数的工作过程。

[c-sharp] view plaincopy
  1. #include <iostream>
  2. using namespace std;
  3. class CExample {
  4. private:
  5. int a;
  6. public:
  7. //构造函数
  8. CExample(int b)
  9. { a = b;}
  10. //拷贝构造函数
  11. CExample(const CExample& C)
  12. {
  13. a = C.a;
  14. }
  15. //一般函数
  16. void Show ()
  17. {
  18. cout<<a<<endl;
  19. }
  20. };
  21. int main()
  22. {
  23. CExample A(100);
  24. CExample B = A; // CExample B(A); 也是一样的
  25. B.Show ();
  26. return 0;
  27. }

CExample(const CExample& C) 就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量

二. 拷贝构造函数的调用时机

在C++中,下面三种对象需要调用拷贝构造函数!
1. 对象以值传递的方式传入函数参数

[c-sharp] view plaincopy
  1. class CExample   
  2. {  
  3. private:  
  4.  int a;  
  5.   
  6. public:  
  7.  //构造函数  
  8.  CExample(int b)  
  9.  {   
  10.   a = b;  
  11.   cout<<"creat: "<<a<<endl;  
  12.  }  
  13.   
  14.  //拷贝构造  
  15.  CExample(const CExample& C)  
  16.  {  
  17.   a = C.a;  
  18.   cout<<"copy"<<endl;  
  19.  }  
  20.    
  21.  //析构函数  
  22.  ~CExample()  
  23.  {  
  24.   cout<< "delete: "<<a<<endl;  
  25.  }  
  26.   
  27.      void Show ()  
  28.  {  
  29.          cout<<a<<endl;  
  30.      }  
  31. };  
  32.   
  33. //全局函数,传入的是对象  
  34. void g_Fun(CExample C)  
  35. {  
  36.  cout<<"test"<<endl;  
  37. }  
  38.   
  39. int main()  
  40. {  
  41.  CExample test(1);  
  42.  //传入对象  
  43.  g_Fun(test);  
  44.   
  45.  return 0;  
  46. }  


调用g_Fun()时,会产生以下几个重要步骤:
(1).test对象传入形参时,会先会产生一个临时变量,就叫 C 吧。
(2).然后调用拷贝构造函数把test的值给C。 整个这两个步骤有点像:CExample C(test);
(3).等g_Fun()执行完后, 析构掉 C 对象。

2. 对象以值传递的方式从函数返回

[c-sharp] view plaincopy
  1. class CExample   
  2. {  
  3. private:  
  4.  int a;  
  5.   
  6. public:  
  7.  //构造函数  
  8.  CExample(int b)  
  9.  {   
  10.   a = b;  
  11.  }  
  12.   
  13.  //拷贝构造  
  14.  CExample(const CExample& C)  
  15.  {  
  16.   a = C.a;  
  17.   cout<<"copy"<<endl;  
  18.  }  
  19.   
  20.      void Show ()  
  21.      {  
  22.          cout<<a<<endl;  
  23.      }  
  24. };  
  25.   
  26. //全局函数  
  27. CExample g_Fun()  
  28. {  
  29.  CExample temp(0);  
  30.  return temp;  
  31. }  
  32.   
  33. int main()  
  34. {  
  35.  g_Fun();  
  36.  return 0;  
  37. }  


当g_Fun()函数执行到return时,会产生以下几个重要步骤:
(1). 先会产生一个临时变量,就叫XXXX吧。
(2). 然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);
(3). 在函数执行到最后先析构temp局部变量。
(4). 等g_Fun()执行完后再析构掉XXXX对象。

3. 对象需要通过另外一个对象进行初始化;

[c-sharp] view plaincopy
  1. CExample A(100);  
  2. CExample B = A;   
  3. // CExample B(A);   

后两句都会调用拷贝构造函数。

三. 浅拷贝和深拷贝

1. 默认拷贝构造函数

很多时候在我们都不知道拷贝构造函数的情况下,传递对象给函数参数或者函数返回对象都能很好的进行,这是因为编译器会给我们自动产生一个拷贝构造函数,这就是“默认拷贝构造函数”,这个构造函数很简单,仅仅使用“老对象”的数据成员的值对“新对象”的数据成员一一进行赋值,它一般具有以下形式:

[c-sharp] view plaincopy
  1. Rect::Rect(const Rect& r)
  2. {
  3. width = r.width;
  4. height = r.height;
  5. }

当然,以上代码不用我们编写,编译器会为我们自动生成。但是如果认为这样就可以解决对象的复制问题,那就错了,让我们来考虑以下一段代码:

[c-sharp] view plaincopy
  1. class Rect
  2. {
  3. public:
  4. Rect()      // 构造函数,计数器加1
  5. {
  6. count++;
  7. }
  8. ~Rect()     // 析构函数,计数器减1
  9. {
  10. count--;
  11. }
  12. static int getCount()       // 返回计数器的值
  13. {
  14. return count;
  15. }
  16. private:
  17. int width;
  18. int height;
  19. static int count;       // 一静态成员做为计数器
  20. };
  21. int Rect::count = 0;        // 初始化计数器
  22. int main()
  23. {
  24. Rect rect1;
  25. cout<<"The count of Rect: "<<Rect::getCount()<<endl;
  26. Rect rect2(rect1);   // 使用rect1复制rect2,此时应该有两个对象
  27. cout<<"The count of Rect: "<<Rect::getCount()<<endl;
  28. return 0;
  29. }

  这段代码对前面的类,加入了一个静态成员,目的是进行计数。在主函数中,首先创建对象rect1,输出此时的对象个数,然后使用rect1复制出对象rect2,再输出此时的对象个数,按照理解,此时应该有两个对象存在,但实际程序运行时,输出的都是1,反应出只有1个对象。此外,在销毁对象时,由于会调用销毁两个对象,类的析构函数会调用两次,此时的计数器将变为负数。

说白了,就是拷贝构造函数没有处理静态数据成员。

出现这些问题最根本就在于在复制对象时,计数器没有递增,我们重新编写拷贝构造函数,如下:

[c-sharp] view plaincopy
  1. class Rect
  2. {
  3. public:
  4. Rect()      // 构造函数,计数器加1
  5. {
  6. count++;
  7. }
  8. Rect(const Rect& r)   // 拷贝构造函数
  9. {
  10. width = r.width;
  11. height = r.height;
  12. count++;          // 计数器加1
  13. }
  14. ~Rect()     // 析构函数,计数器减1
  15. {
  16. count--;
  17. }
  18. static int getCount()   // 返回计数器的值
  19. {
  20. return count;
  21. }
  22. private:
  23. int width;
  24. int height;
  25. static int count;       // 一静态成员做为计数器
  26. };

2. 浅拷贝

所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。大多情况下“浅拷贝”已经能很好地工作了,但是一旦对象存在了动态成员,那么浅拷贝就会出问题了,让我们考虑如下一段代码:

[c-sharp] view plaincopy
  1. class Rect
  2. {
  3. public:
  4. Rect()      // 构造函数,p指向堆中分配的一空间
  5. {
  6. p = new int(100);
  7. }
  8. ~Rect()     // 析构函数,释放动态分配的空间
  9. {
  10. if(p != NULL)
  11. {
  12. delete p;
  13. }
  14. }
  15. private:
  16. int width;
  17. int height;
  18. int *p;     // 一指针成员
  19. };
  20. int main()
  21. {
  22. Rect rect1;
  23. Rect rect2(rect1);   // 复制对象
  24. return 0;
  25. }

在这段代码运行结束之前,会出现一个运行错误。原因就在于在进行对象复制时,对于动态分配的内容没有进行正确的操作。我们来分析一下:

在运行定义rect1对象后,由于在构造函数中有一个动态分配的语句,因此执行后的内存情况大致如下:

在使用rect1复制rect2时,由于执行的是浅拷贝,只是将成员的值进行赋值,这时 rect1.p = rect2.p,也即这两个指针指向了堆里的同一个空间,如下图所示:

当然,这不是我们所期望的结果,在销毁对象时,两个对象的析构函数将对同一个内存空间释放两次,这就是错误出现的原因。我们需要的不是两个p有相同的值,而是两个p指向的空间有相同的值,解决办法就是使用“深拷贝”。

3. 深拷贝

在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间,如上面的例子就应该按照如下的方式进行处理:

[c-sharp] view plaincopy
  1. class Rect
  2. {
  3. public:
  4. Rect()      // 构造函数,p指向堆中分配的一空间
  5. {
  6. p = new int(100);
  7. }
  8. Rect(const Rect& r)
  9. {
  10. width = r.width;
  11. height = r.height;
  12. p = new int;    // 为新对象重新动态分配空间
  13. *p = *(r.p);
  14. }
  15. ~Rect()     // 析构函数,释放动态分配的空间
  16. {
  17. if(p != NULL)
  18. {
  19. delete p;
  20. }
  21. }
  22. private:
  23. int width;
  24. int height;
  25. int *p;     // 一指针成员
  26. };

此时,在完成对象的复制后,内存的一个大致情况如下:

此时rect1的p和rect2的p各自指向一段内存空间,但它们指向的空间具有相同的内容,这就是所谓的“深拷贝”。

3. 防止默认拷贝发生

通过对对象复制的分析,我们发现对象的复制大多在进行“值传递”时发生,这里有一个小技巧可以防止按值传递——声明一个私有拷贝构造函数。甚至不必去定义这个拷贝构造函数,这样因为拷贝构造函数是私有的,如果用户试图按值传递或函数返回该类对象,将得到一个编译错误,从而可以避免按值传递或返回对象。

[c-sharp] view plaincopy
  1. // 防止按值传递
  2. class CExample
  3. {
  4. private:
  5. int a;
  6. public:
  7. //构造函数
  8. CExample(int b)
  9. {
  10. a = b;
  11. cout<<"creat: "<<a<<endl;
  12. }
  13. private:
  14. //拷贝构造,只是声明
  15. CExample(const CExample& C);
  16. public:
  17. ~CExample()
  18. {
  19. cout<< "delete: "<<a<<endl;
  20. }
  21. void Show ()
  22. {
  23. cout<<a<<endl;
  24. }
  25. };
  26. //全局函数
  27. void g_Fun(CExample C)
  28. {
  29. cout<<"test"<<endl;
  30. }
  31. int main()
  32. {
  33. CExample test(1);
  34. //g_Fun(test); 按值传递将出错
  35. return 0;
  36. }

四. 拷贝构造函数的几个细节

1. 拷贝构造函数里能调用private成员变量吗?
解答:
这个问题是在网上见的,当时一下子有点晕。其时从名子我们就知道拷贝构造函数其时就是一个特殊的构造函数,操作的还是自己类的成员变量,所以不受private的限制。

2. 以下函数哪个是拷贝构造函数,为什么?

[c-sharp] view plaincopy
  1. X::X(const X&);
  2. X::X(X);
  3. X::X(X&, int a=1);
  4. X::X(X&, int a=1, int b=2);


解答:对于一个类X, 如果一个构造函数的第一个参数是下列之一:
a) X&
b) const X&
c) volatile X&
d) const volatile X&
且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数.

[c-sharp] view plaincopy
  1. X::X(const X&);  //是拷贝构造函数
  2. X::X(X&, int=1); //是拷贝构造函数
  3. X::X(X&, int a=1, int b=2); //当然也是拷贝构造函数

3. 一个类中可以存在多于一个的拷贝构造函数吗?
解答:
类中可以存在超过一个拷贝构造函数。

[c-sharp] view plaincopy
  1. class X {
  2. public:
  3. X(const X&);      // const 的拷贝构造
  4. X(X&);            // 非const的拷贝构造
  5. };

注意,如果一个类中只存在一个参数为 X& 的拷贝构造函数,那么就不能使用const X或volatile X的对象实行拷贝初始化.

[c-sharp] view plaincopy
  1. class X {
  2. public:
  3. X();
  4. X(X&);
  5. };
  6. const X cx;
  7. X x = cx;    // error

如果一个类中没有定义拷贝构造函数,那么编译器会自动产生一个默认的拷贝构造函数。
这个默认的参数可能为 X::X(const X&)或 X::X(X&),由编译器根据上下文决定选择哪一个。

版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 上一篇c/c++中typedef详解
  • 下一篇C++类型转换详解--const_cast
主题推荐
对象结构类classcolor
猜你在找
数据结构和算法
C语言及程序设计初步
Part 1:基础语言-Cocos2d-x手机游戏开发必备C++语言基础
Hadoop 2.X企业级开发入门系列
深入浅出MySQL入门必备
Stringc++详解
VC++常用数据类型及其操作详解非常经典共同分享
VC++常用数据类型及其操作详解非常经典共同分享
VC++常用数据类型及其操作详解
VC++常用数据类型及其操作详解
准备好了么? 跳吧             !更多职位尽在 CSDN JOB
windows C/C++开发工程师
北京博睿宏远科技发展有限公司

|

6-10K/月

我要跳槽

C++开发工程师
上海晶赞科技发展有限公司

|

12-24K/月

我要跳槽

美国C++软件工程师
北京玛赫西计算机教育咨询有限公司

|

40-80K/月

我要跳槽

linux C/C++开发
中国科学院通用芯片与基础软件研究中心

|

15-20K/月

我要跳槽

id="ad_frm_0" frameborder="0" scrolling="no" src="http://blog.csdn.net/common/ad.html?t=4&containerId=ad_cen&frmId=ad_frm_0" style="border-width: 0px; overflow: hidden; width: 746px; height: 90px;">
查看评论
123楼 iamtyk 2015-06-21 08:13发表 [回复]
楼主
X::X(const X&)或 X::X(X&),
X&是什么参数
122楼 iamtyk 2015-06-21 08:12发表 [回复]
X::X(const X&)或 X::X(X&),
楼主..X&是什么参数
121楼 zhengjingsen 2015-06-03 11:12发表 [回复]
讲的特别详细,让我搞清楚了一个问题
120楼 yinglinghuang888888 2015-06-03 10:54发表 [回复]
深度了解了复制构造函数,感谢楼主。
119楼 BGY_Bluesky 2015-05-28 15:32发表 [回复]
非常好的帖子,仔细阅读并跟着上机实践,真是受益匪浅,感谢分享,终于深刻理解类的拷贝构造函数了,赞赞 赞
118楼 SmilingSunrise 2015-05-13 16:36发表 [回复]
不错,写的很详细具体!
117楼 veryitman 2015-05-09 22:10发表 [回复]
辛苦楼主了.赞一个!
不过你的例子应该是在 vs 上面验证的,在 mac 平台下 xcode 编译运行有点不一样.
116楼 Felix_0920 2015-05-08 17:30发表 [回复]
请教楼主一个问题,对于用户自定义的类,如果没有默认的无参构造函数,那么编译器是如果生成类的临时变量对象的?
115楼 sinat_27624023 2015-04-22 16:36发表 [回复]
博主,想请教你一个问题。拷贝构造函数能将一个对象的string类字符串拷贝给另一个对象吗?
114楼 CourageK 2015-04-02 09:35发表 [回复]
楼主真是太有心了,让好多人都对复制构造函数有了很深入的理解,从此面试此类问题不再纠结了。谢谢你~
113楼 编程艺术家 2015-03-29 16:00发表 [回复]
mark
112楼 qszchew 2015-03-17 19:36发表 [回复]
讲得真好!以前一直搞不懂的!
111楼 li1191863273 2015-02-11 03:04发表 [回复]
受教了
110楼 mz490848173 2015-02-09 13:45发表 [回复]
想问一下 那个 复制构造函数 所复制的函数 是在 堆内存里 吗 还是 栈内存里
109楼 小米mimica 2015-01-15 20:21发表 [回复]
写的非常详细,也很易于理解!受益匪浅!
108楼 jinjinwu 2015-01-14 15:45发表 [回复]
看书一直没看懂,博主讲的很清楚啊,
107楼 Sungyers 2015-01-10 15:54发表 [回复]
博主给力,狂赞!
106楼 louObaichu 2015-01-06 15:28发表 [回复]
mark
105楼 ColaWJY 2014-12-16 21:54发表 [回复]
给力!Mark了!
104楼 Huai-yu 2014-12-04 09:05发表 [回复]
支持楼主,赞一个
103楼 御弟叔叔 2014-11-29 14:13发表 [回复]
赞一个!
102楼 _LebronLu_ 2014-11-28 11:16发表 [回复]
很详细,佩服!
101楼 jyf823691221 2014-11-13 17:00发表 [回复]
赞一个 写的太好了 豁然开朗呀
100楼 eyeshot_yang33 2014-11-06 10:16发表 [回复]
谢谢,看完豁然开朗了!
99楼 hityxhvp 2014-11-05 21:48发表 [回复]
通读全文,非常感谢!
98楼 tandyx 2014-11-03 17:10发表 [回复]
很不错, 非常给力。 特地登陆来点赞。 我的密码都试了好多次才登上来。 哈哈。
97楼 zhaolianyun 2014-10-26 20:28发表 [回复]
真心不错,非常受教!!!
96楼 wjwizard 2014-10-23 19:05发表 [回复]
赞一个,好贴
95楼 eternal_tune 2014-10-14 12:41发表 [回复]
CExample B = A; // CExample B(A); 也是一样的 
这段代码应该是赋值构造函数(操作符为‘=’)。如果将此代码放在private里面就编译不过:
CExample & operator=(const CExample&);

CExample B(A);此段才是调用的拷贝构造函数

Re: wobushixiaomi 2014-12-31 14:31发表 [回复]
回复u012844596:正解!
94楼 Sylvernass 2014-10-13 19:21发表 [回复]
写的真心很不错,这部分对初学者来说会搞的有点混,但是看了这篇文章,顿时明白了很多,很清晰,不得不来留言点赞
93楼 royliu1 2014-10-11 12:54发表 [回复]
写得好! 顺便看了下前辈们的讨论,有收获! 谢谢
92楼 Edwin404 2014-09-30 16:33发表 [回复]
问个问题,为啥“2. 对象以值传递的方式从函数返回”这个标题下面那个例子什么都没有打印呢?
91楼 lichangyu2011 2014-09-29 10:05发表 [回复]
转载了!学知识真应该这样,受教了!惭愧一个!!!
90楼 puppylpg 2014-09-18 20:09发表 [回复]
太详细了!果断赞起!
89楼 terry_o0o 2014-09-17 16:37发表 [回复]
一直不太理解,就找到lz这个文章了,豁然开朗!
88楼 jiaqingmin1990 2014-09-15 10:50发表 [回复]
nice....
87楼 wangxm1111 2014-09-14 16:49发表 [回复]
一下子思路很清晰,讲的很不错
86楼 图像配准菜鸟 2014-09-13 10:36发表 [回复]
受教了!谢谢你耐心的讲解!
85楼 他们叫我周周周 2014-09-12 14:09发表 [回复]
太棒了~
84楼 忆之独秀 2014-09-06 16:16发表 [回复]
写的不能更好
83楼 贪睡的萝卜 2014-09-05 16:43发表 [回复]
受益匪浅,感谢博主
82楼 叶子399 2014-09-02 11:03发表 [回复]
大赞~
81楼 旭梦1990 2014-08-25 08:44发表 [回复]
太好了,了解了拷贝构造函数
80楼 uiong8163 2014-08-09 00:38发表 [回复]
大神,很感谢!
79楼 panjunnn 2014-08-07 14:47发表 [回复]
受教了,好啊
78楼 坚决不做程序狗 2014-08-07 08:48发表 [回复]
牛逼。。。。学习了
77楼 PushFang 2014-07-30 22:36发表 [回复]
大赞!!!!
76楼 笃志近思 2014-07-25 17:16发表 [回复]
受教了,写的非常棒!
75楼 bin03 2014-07-21 11:30发表 [回复]
很好。条理清晰。
74楼 某种意境 2014-07-09 23:16发表 [回复]
终于晓得这玩意儿了
73楼 junjie58msp 2014-07-02 11:08发表 [回复]
此文写的太好了。赞一个。
72楼 HUASHUIXIAOHAI 2014-06-27 14:49发表 [回复]
楼主写的真心不错,大赞!
71楼 xanarry 2014-06-19 09:12发表 [回复]
受益不浅,谢谢博主
70楼 卿笃军 2014-05-25 08:47发表 [回复]
拷贝构造函数:
A(A a){}
值传递传参为什么会编译通不过?
Re: heipacker 2014-06-04 23:11发表 [回复]
回复u012339743:这个不是复制构造函数
Re: 卿笃军 2014-06-05 22:18发表 [回复]
回复fupacker:我只是想问这种传参方式 编译为何通不过。
Re: heipacker 2014-06-06 23:11发表 [回复]
回复u012339743:编译不通过是因为A a这里的定义问题,只有在看到一个类的定义的时候才能把一个数据成员声明为这个类型的对象,
但是当一个类的类头被看到时,它就被视为已经被声明了,所以一个类可以用指向自身类型的指针或引用作为数据成员

这里数据成员你也可以认为是一个变量的定义 看看C++ primer

Re: 卿笃军 2014-06-07 00:25发表 [回复]
回复fupacker:这个我明白。就像结构体一样:
struct node 
{
struct node next; //错误
};
struct node 
{
struct node *next; //正确
};
————————————
但是:
class A
{
public:
A(A a); //错误
void Print(A a){};//正确
};
这个是如何解释?
Re: gdouse1093 2014-09-28 22:02发表 [回复]
回复u012339743:如果不是引用会造成无限递归, (会无限的调用拷贝构造函数)
Re: heipacker 2014-06-07 13:45发表 [回复]
回复u012339743:拷贝构造函数一定要使用引用传递 你查一下就知道了
Re: 卿笃军 2014-06-07 14:55发表 [回复]
回复fupacker:这个我查过,也明白A(A a)这样会带来的问题。
就是上面那个问题有些不明白。
Re: 小王王小 2014-08-04 16:37发表 [回复]
回复u012339743:拷贝构造函数如果允许值传递,那么实参对象传递给形参对象,会拥有两个存储空间,新创建的临时存储空间对象初始化时,同样需要调用拷贝构造函数,以此类推,会永无休止的调用拷贝构造函数,最终会导致栈溢出。
Re: 卿笃军 2014-09-28 23:42发表 [回复]
回复wangyong19881123:我明白了。
69楼 动感蚂蚁 2014-05-15 13:03发表 [回复]
学习了,谢谢
68楼 tian15134549098 2014-04-21 20:41发表 [回复]
超级给力,赞
67楼 lvjing2 2014-04-20 10:52发表 [回复]
必须赞一个!
66楼 吉米芽菜 2014-03-30 10:49发表 [回复]
衷心地赞一个
65楼 松木 2014-03-23 18:06发表 [回复]
楼主分析得太好了,让我对拷贝构造函数有了更深的认识,赞!
64楼 雨中奔跑 2014-02-17 11:55发表 [回复]
很详细nice
63楼 loe 2013-12-30 17:27发表 [回复]
mark!
62楼 feierfeierfly 2013-12-25 16:20发表 [回复]
受教了!谢谢楼主!
61楼 无之念 2013-12-23 17:13发表 [回复]
受教了
60楼 chuyuanjie123 2013-12-16 22:14发表 [回复]
很好。思路清晰
59楼 曾是土木人 2013-12-15 00:19发表 [回复]
总结的很好
58楼 lcjwxd 2013-12-05 15:39发表 [回复]
初学者也来赞一个,以后好好学习,天天向上!
57楼 zzqq0103 2013-12-04 17:20发表 [回复]
真的说的很好,受教了!!
56楼 苍原狮啸 2013-11-27 11:58发表 [回复]
后文的添加的内容很给力
55楼 HXD974287503 2013-11-21 16:17发表 [回复]
好好!
54楼 virginia-qing 2013-11-21 14:29发表 [回复]
good
53楼 zhw_CZ 2013-11-20 22:59发表 [回复]
写的真好,谢谢。
52楼 yulu27 2013-11-11 16:37发表 [回复]
写的真不错
51楼 tyy_ing 2013-11-10 20:58发表 [回复]
写的很棒 怒赞
50楼 code_feng 2013-11-05 15:29发表 [回复]
学习了……谢谢楼主~
49楼 黑白尤文 2013-08-31 09:16发表 [回复]
请问文章中的图是用的什么工具画的啊?
Re: 黑白尤文 2013-09-14 22:34发表 [回复]
回复juvenfan:这么强大。。。 我用visio画出来的图都很丑……
Re: faith19871023 2013-09-07 15:34发表 [回复]
回复juvenfan:visio就可以啊
48楼 chz429 2013-08-25 16:06发表 [回复]
先赞一个~
关于对象以值传递的方式从函数返回会调用拷贝构造函数,怎么在Devc++测试的时候从函数返回不会调用拷贝构造函数,而在Visual Stdio却调用了呢

[cpp] view plaincopy
  1. #include<iostream>
  2. using namespace std;
  3. struct Node
  4. {
  5. int a, b;
  6. Node(int i, int j):a(i), b(j){}
  7. Node(const Node& a)
  8. {
  9. cout << "copy" << endl;
  10. }
  11. };
  12. Node fun()
  13. {
  14. Node n = Node(1,2);
  15. return n;
  16. }
  17. int main()
  18. {
  19. fun();
  20. system("pause");
  21. }
Re: whyisyoung 2014-11-25 23:05发表 [回复]
回复chz429:g++ 3.2 以上版本对编译器做了优化,导致第三种值返回的情况不调用拷贝构造函数, mac 上也不会调用, vs2012 仍调用,
47楼 lmnlkm 2013-08-22 16:18发表 [回复]
受教了,谢谢
46楼 鸭梨小乖宝宝 2013-08-15 18:35发表 [回复]
非常好的一篇文章,清晰又有条理
45楼 夏雪冬日 2013-08-10 14:48发表 [回复]
写的简单、清晰。还不错!
44楼 a429051366 2013-08-06 12:40发表 [回复]
真心希望楼主能写一本关于c++的书 ,业界的书可谓汗牛充栋,讲的明白清楚的寥若晨星,大部分的书都是讲讲语法,再贴上几个例子,看的人似懂非懂,云里雾里,整本书好像条理清楚,脉络清晰其实一塌糊涂,外国人的书又好点,可是看外文的大部头真的要耐心的,能说的这么清楚明白太难得了,真心希望楼主写本书 ,不写语法什么的 ,就和论坛里面的这种一样,真心的牛啊,要能给做几个c++的视频就更好了,赞一个
43楼 wsb929 2013-08-04 21:10发表 [回复]
顶 受教了 !! 谢谢!!!
42楼 五月初五 2013-08-03 11:53发表 [回复]
很好,受教了
41楼 云亦无心 2013-07-26 15:58发表 [回复]
讲的很有条理,分析透彻~
40楼 xiaominglang 2013-07-24 16:33发表 [回复]
受教了!!!楼主大牛!!!
39楼 __JacHan 2013-07-23 08:23发表 [回复]
学习啦,顶哥们,用的时候才能发现问题,有问题了再来跟哥们讨论
38楼 4unreal 2013-07-06 16:08发表 [回复]
很赞的文章
37楼 wennuan80 2013-06-25 09:24发表 [回复]
本人菜鸟,想问一下:
拷贝构造函数这样写行吗?
//拷贝构造函数
CExample(const CExample *C)
{
a = C->a;
}
36楼 wennuan80 2013-06-25 09:03发表 [回复]
为什么要用引用,指针不行吗?
35楼 MFCclassxiao 2013-06-18 10:25发表 [回复]
学习了,思路清晰了很多,谢谢大牛
34楼 wanhuatong1987 2013-06-14 10:02发表 [回复]
[cpp] view plaincopy
  1. Rect(const Rect& r)
  2. {
  3. width = r.width;
  4. height = r.height;
  5. p = new int;    // 为新对象重新动态分配空间
  6. *p = *(r.p);
  7. }

这样写在程序结束的时候调用了两次析构函数,不存在33楼说的内存泄露问题,这是用VS2010调试的结果。

33楼 MrSimp1e 2013-06-03 15:08发表 [回复]
深拷贝这里, 
Rect(const Rect& r) 

width = r.width; 
height = r.height; 
p = new int; // 为新对象重新动态分配空间 
*p = *(r.p); 
}

P重新分配了动态空间,但是原来的空间却没有释放掉。这样就造成了内存泄露吧。
Rect(const Rect& r) 

width = r.width; 
height = r.height; 
int *pTemp = p; // 将p指针指向的内存地址缓存起来
p = new int; // 为新对象重新动态分配空间 
*p = *(r.p); 
delete pTemp; // 释放p原来的内存

只是探讨,没有其他意思。

Re: Atlas 2013-06-18 09:28发表 [回复]
引用“bboyfeiyu”的评论:深拷贝这里, 
Rect(const Rect&amp; r) 

...
这样写的程序中,不会内存泄漏,但是另外一个问题,如果申请空间失败,但之前改变你实例中的部分,因为这里width = r.width; height = r.height; 已经执行了,是不是违反了异常安全性,这个怎么看?
Re: wanhuatong1987 2013-06-14 10:09发表 [回复]
回复bboyfeiyu:博友,你这样写的话会出现“ 0xC0000005: Access violation reading location 0xccccccc0.”错误,这是VS2010调试的结果。其实,文中那样写在退出程序的时候会调用两次析构函数,不会存在内存泄漏的问题。如有不对的地方,请指教。
Re: MrSimp1e 2013-06-14 12:46发表 [回复]
回复wanhuatong1987:gcc倒是不会报错,改成这样应该没有问题。
P重新分配了动态空间,但是原来的空间却没有释放掉。这样就造成了内存泄露吧。
Rect(const Rect& r) 

width = r.width; 
height = r.height; 
int *pTemp = p; // 将p指针指向的内存地址缓存起来,局部指针变量
p = new int; // 为新对象重新动态分配空间 
*p = *(r.p); 
}
Re: kkkwjx 2014-09-06 12:35发表 [回复]
回复bboyfeiyu:我认为不会出现内存泄露。
首先既然是构造函数,p最初就是一个野值,没有指向分配的内存,所以无需考虑释放原来的内存,直接进行内存分配即可。注意是直接分配不是重新分配。
反而觉得您的代码有问题,delete了一个野值,可能会出现问题。
32楼 LTheMiracle 2013-05-10 10:10发表 [回复]
挺有用的,谢谢!
31楼 蛋疼的 2013-04-22 15:20发表 [回复]
除非返回值的临时变量是在函数刚进入时先入栈。。
Re: NBHH0329 2014-07-25 16:57发表 [回复]
回复u010050719:从汇编上来说确实如此。临时变量XXX生成的汇编语句是更靠前的。
30楼 蛋疼的 2013-04-22 15:16发表 [回复]
(1). 先会产生一个临时变量,就叫XXXX吧。
(2). 然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);
(3). 在函数执行到最后先析构temp局部变量。
(4). 等g_Fun()执行完后再析构掉XXXX对象。
个人觉得先析构xxxx对象再析构temp局部变量
函数返回值如果大于8字节,就要创建一个临时内存来放临时变量,按照堆栈的先进后出原则,temp应该是先入栈,xxxx这个临时变量(其实只能叫做指针,指向一块存放该类型变量的内存)后入栈,这个指针指向的地址会存放在寄存器中,函数返回时寄存器EAX根据这个地址去找到临时变量的内容,你也许会说那函数返回,该函数的堆栈祯已经销毁,如何返回该临时变量的值,堆栈祯是被销毁了,但是程序不会清空其中的值,临时变量所在内存值依然存在。
Re: heipacker 2014-06-06 23:23发表 [回复]
回复u010050719:你自己分析的结论也是先析构的temp再析构XXX啊
Re: 江浙沪小团队移动外包 2013-10-11 15:34发表 [回复]
回复u010050719:本人亲测的确是先析构掉了函数内的局部变量,然后程序执行完后才析构函数返回时调用拷贝构造函数产生的临时对象。
Re: 夏雪冬日 2013-08-10 14:46发表 [回复]
回复u010050719:分析的到位。我的观点跟你一样!
29楼 蛋疼的 2013-04-22 15:03发表 [回复]
(1). 先会产生一个临时变量,就叫XXXX吧。
(2). 然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);
(3). 在函数执行到最后先析构temp局部变量。
(4). 等g_Fun()执行完后再析构掉XXXX对象。

第三和第四是不是顺序反了?先析构xxxx对象再析构temp对象

28楼 chaoguo1234 2013-04-18 16:26发表 [回复]
很好,不错
27楼 chm626905227 2013-04-12 17:28发表 [回复]
很好的一篇学习拷贝构造函数的文章,顶一个~
26楼 曦花 2013-04-08 13:50发表 [回复]
为什么要加const?
为什么要用引用?指针行吗?
25楼 hulaquandequan 2013-04-07 16:15发表 [回复]
真心不错~ 感谢~
24楼 hubi 2013-03-22 16:10发表 [回复]
棒!!!!!!!!!
23楼 jackielzjn 2013-03-14 14:42发表 [回复]
谢谢楼主,高手就是不一样。顶一个。。。。。。。
22楼 jouyouwen 2013-03-10 16:51发表 [回复]
写的很好,受教了,谢谢!
21楼 FranklinLING 2013-03-08 20:32发表 [回复]
哈哈,和其它评论有同感:简单、透彻!多谢!!
20楼 她大哥 2013-03-04 14:51发表 [回复]
写的太好了
19楼 waldobear 2013-03-02 18:14发表 [回复]
很清楚,收获不小。
18楼 na_nalove_huahua 2013-02-22 18:25发表 [回复]
文章写的很棒 解答了我很多疑惑
17楼 fly2sky 2013-02-21 23:11发表 [回复]
2中的问题又验证了一下,在vc下就会调用拷贝构造函数,在linux下用g++编译则不会调用拷贝构造函数,看来和编译器有关,文章很给力,学习了
16楼 fly2sky 2013-02-21 22:48发表 [回复]
2. 对象以值传递的方式从函数返回 这个给的例子验证了一下,并没有调用到拷贝构造函数,请博主试一下
15楼 Ritter_Liu 2013-02-19 20:03发表 [回复]
此文写的太好了
14楼 青茶柠檬 2013-01-30 14:36发表 [回复]
谢谢楼主,讲的很清楚。转走了
13楼 skkks 2013-01-20 10:21发表 [回复]
条理很清晰,喜欢这样的风格,谢谢~~
12楼 GrimRaider 2013-01-09 11:22发表 [回复]
I like it! Thank you!
11楼 windyrain999 2012-12-23 12:19发表 [回复]
谢谢您,对我很有帮助!!!
10楼 nashouat 2012-12-22 10:07发表 [回复]
太好了,讲得很通俗易懂,很容易理解,谢谢楼主,真的感谢!现在对拷贝构造函数清晰多了。谢谢!
9楼 _北方的雪_ 2012-12-19 11:05发表 [回复]
对(四)中第一个问题也是有疑问:在拷贝构造函数中,形参是对象的引用,在函数体中怎么可以直接用对象调用私有成员呢?
Re: 夏雪冬日 2013-08-10 14:43发表 [回复]
回复sgs1018:拷贝构造函数是一种特殊的构造函数。类的成员函数是可以访问该类的private成员的。
8楼 yintangzengli 2012-12-05 09:52发表 [回复]
写的蛮不错的,例子有对比,真的是让我看进去了的。
7楼 zhengtong0416 2012-11-17 11:40发表 [回复]
受教!向您学习
6楼 lwbeyond 2012-10-26 13:06发表 [回复]
共同进步啊
5楼 LinuxMan 2012-10-24 09:16发表 [回复]
很容易理解,赞一个
4楼 骑着乌龟去看海 2012-10-24 09:00发表 [回复]
MK
3楼 hanzhijun58 2012-10-15 20:25发表 [回复]
受教了
2楼 MyLend 2012-10-05 22:01发表 [回复]
牛啊。。。
1楼 aoshikang 2011-04-21 16:53发表 [回复]
哥们,你这篇文章真给力!受教了!谢谢。
发表评论
  • 用 户 名:
  • u011562836
  • 评论内容:
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

id="ad_frm_1" frameborder="0" scrolling="no" src="http://blog.csdn.net/common/ad.html?t=5&containerId=ad_bot&frmId=ad_frm_1" style="border-width: 0px; overflow: hidden; width: 746px; height: 0px;">
核心技术类目
全部主题 Hadoop AWS 移动游戏 Java Android iOS Swift 智能硬件 DockerOpenStack VPN Spark ERP IE10 Eclipse CRM JavaScript 数据库 Ubuntu NFCWAP jQuery BI HTML5 Spring Apache .NET API HTML SDK IIS FedoraXML LBS Unity Splashtop UML components Windows Mobile Rails QEMU KDECassandra CloudStack FTC coremail OPhone CouchBase 云计算 iOS6 RackspaceWeb App SpringSide Maemo Compuware 大数据 aptech Perl Tornado RubyHibernate ThinkPHP HBase Pure Solr Angular Cloud Foundry Redis ScalaDjango Bootstrap

  • 个人资料
  •  
    lwbeyond
     
     
    • 访问:329953次
    • 积分:4418
    • 等级: 
    • 排名:第2978名
    • 原创:165篇
    • 转载:29篇
    • 译文:1篇
    • 评论:221条
  • 文章搜索
  • 博客专栏
  • STL学习笔记

    文章:16篇

    阅读:17506

  • 文章分类
  • C/C++(44)
  • Linux Shell(4)
  • STL(17)
  • Linux(33)
  • vxWorsk(3)
  • ACE(4)
  • Eclipse(2)
  • English(3)
  • 设计模式(25)
  • 网络编程(14)
  • 汽车电子(4)
  • 加密算法(1)
  • 正则表达式(2)
  • 生活(2)
  • 思维改变生活(4)
  • 嵌入式linux开发(8)
  • QT(14)
  • uC/OS-II(6)
  • cocos2d(1)
  • Java(6)
  • 文章存档
    • 2015年03月(3)
    • 2015年02月(4)
    • 2014年11月(8)
    • 2014年10月(2)
    • 2013年12月(1)
      展开
  • 阅读排行
  • C++拷贝构造函数详解(90856)
  • 如何正确的关闭 MFC 线程(14773)
  • C++类型转换详解--const_cast(8325)
  • 学习C++该看什么书?(6062)
  • SPI 协议(4545)
  • 汽车诊断简介(4240)
  • GDB调试精粹(4040)
  • VxWorks学习笔记 -- 信号量(3971)
  • QT 调用外部程序(3740)
  • 学习 effortless English 20天了(3366)
  • 评论排行
  • C++拷贝构造函数详解(145)
  • 如何正确的关闭 MFC 线程(7)
  • C++类型转换详解--const_cast(6)
  • 详解C++ friend关键字(6)
  • VxWorks学习笔记 -- 信号量(3)
  • strcpy 详解(3)
  • STL编程轻松入门(3)
  • sizeof 精要(3)
  • 自定义 Base64 加密算法(2)
  • 线程的等待(2)
  • 推荐文章
  • id="ad_frm_2" frameborder="0" scrolling="no" src="http://blog.csdn.net/common/ad.html?t=12&containerId=ad_commend&frmId=ad_frm_2" style="border-width: 0px; overflow: hidden; width: 182px; height: 200px;">
  • 最新评论
  • C++拷贝构造函数详解

    iamtyk: 楼主X::X(const X&)或 X::X(X&),X&是什么参数

  • C++拷贝构造函数详解

    iamtyk: X::X(const X&)或 X::X(X&),楼主..X&是什么参数

  • GDB调试精粹

    xiaozhaoying: 你好,“四、设置与清除断点”clean那里应该写错了,应该改为clear

  • windows下安装cocos2d-x-2.2.5

    夏至北国: 请问下,基于c++的怎么打包成apk啊

  • C++拷贝构造函数详解

    zhengjingsen: 讲的特别详细,让我搞清楚了一个问题

  • C++拷贝构造函数详解

    yinglinghuang888888: 深度了解了复制构造函数,感谢楼主。

  • C++拷贝构造函数详解

    BGY_Bluesky: 非常好的帖子,仔细阅读并跟着上机实践,真是受益匪浅,感谢分享,终于深刻理解类的拷贝构造函数了,赞赞 ...

  • C++拷贝构造函数详解

    SmilingSunrise: 不错,写的很详细具体!

  • GDB调试精粹

    菜鸟实验室: 帝仰大学的?

  • C++拷贝构造函数详解

    veryitman: 辛苦楼主了.赞一个!不过你的例子应该是在 vs 上面验证的,在 mac 平台下 xcode 编译运行...

公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告|合作伙伴|论坛反馈
网站客服杂志客服微博客服webmaster@csdn.net400-600-2320|北京创新乐知信息技术有限公司 版权所有|江苏乐知网络技术有限公司 提供商务支持
京 ICP 证 070598 号|Copyright © 1999-2014, CSDN.NET, All Rights Reserved 

备忘录_C++_拷贝构造函数相关推荐

  1. 利用类定义一个指针会调用默认构造函数吗_C++的拷贝构造函数

    拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象.拷贝构造函数通常用于: 通过使用另一个同类型的对象来初始化新创建的对象. 复制对象把它作为参数传递给 ...

  2. 不存在从void转换到sqlist的适当构造函数_拷贝构造函数与赋值构造函数

    拷贝构造函数与赋值构造函数 在C++中,如果要创建一个新的类,并用已有的类来给它附初值.就要用到拷贝构造函数,拷贝构造函数又分为两种. 1.合成的拷贝构造函数 在你没有定义自己的拷贝构造函数而又调用了 ...

  3. Cpp / 拷贝构造函数的参数为什么必须使用引用类型

    表面原因:编译器无法通过,会报如下错误: error: invalid constructor; you probably meant 'CExample (const CExample&)' ...

  4. c++ 拷贝构造函数_禁止拷贝构造,禁止bug

    禁止拷贝构造,禁止bug 一.前言 首先,我先讲讲为什么会写这篇文章:这个也是翻阅自己之前博客,当时看开源代码的时候,总是很奇怪,为什么有的代码中会会出现类似于Epoll( const Epoll&a ...

  5. 复制构造函数(拷贝构造函数)

    也许很多C++的初学者都知道什么是构造函数,但是对复制构造函数(copy constructor)却还很陌生.对于我来说,在写代码的时候能用得上复制构造函数的机会并不多,不过这并不说明复制构造函数没什 ...

  6. C++ 拷贝构造函数和赋值运算符

    本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数.什么情况下调用赋值运算符.最后,简单的分析了下深拷贝和浅拷贝的问题. 拷贝构造函数和赋值运算符 在默认情况下(用户没有定义 ...

  7. 面试准备每日五题:C++(七)——左值右值、面向对象、四种cast转换、拷贝构造函数赋值、虚函数多态

    文章目录 一. 什么是右值引用,跟左值又有什么区别? 二. 面向对象的三大特征 三. c++中四种cast转换 四.拷贝构造函数和赋值运算符的认识 五. 对虚函数和多态的理解 一. 什么是右值引用,跟 ...

  8. 【C++ 6.析构函数和拷贝构造函数】

    C++ 6.析构函数和拷贝构造函数 1. 析构函数 a. 概念 b. 特性 1.析构函数是在类名称前面加上字符~. 2.无参数和返回值. 3.一个类有且只有一个析构函数. 4.对象生命周期结束时,C+ ...

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

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

最新文章

  1. element ui 红点_element-ui 自定义表单验证 , 但是不出现小红心了
  2. java的xms与xmx和服务器内存_JAVA_OPTS参数-Xms和-Xmx的作用
  3. 编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推。
  4. spring boot: spring Aware的目的是为了让Bean获得Spring容器的服务
  5. transformers BertModel
  6. Jenkins-FQA
  7. ###Tomcat目录介绍和基础
  8. 三人抢答器逻辑电路图_数字电路3人抢答器实验报告.doc
  9. 黑马程序员java学生管理系统
  10. 按键精灵手机助手学习笔记
  11. 框架设计--第十章 MyBatis与Spring的整合--习题答案
  12. Java集成流行的打印插件lodop
  13. Pytorch加载模型并进行图像分类预测
  14. 水果店收银系统解决方案
  15. sequoia负载均衡
  16. Chrome浏览器查看、找回保存的网站账户密码
  17. Kubernetes安装系列之kubctl exec权限设定
  18. 【单元复习】之标日初级下册第九、十单元
  19. 利用层次聚类算法进行基于基站定位数据的商圈分析
  20. python获取根目录位置

热门文章

  1. css3满天金光飞舞效果,CSS3金光四射的钥匙
  2. 软件测试技能大赛教学平台
  3. Windows Subsystem for Linux安装与使用
  4. 一个人负债累累走投无路、每天都在被催收,怎么办?看这里
  5. 谷歌小恐龙更新啦!!!!!!(加入奥运会项目赛马,游泳,体操等)快来体验a哈哈哈哈哈哈哈哈
  6. 地质雷达物理测量RADAN®7软件(Radan 7.6.19.11260)最新下载
  7. 【Win10系统】Win10系统无线网消失,更改适配器只有以太网,找不到WLAN怎么解决?
  8. python协程和线程_python之并发编程(线程\进程\协程)
  9. linux wifi管理工具下载,(Linux无线网卡WIFI上网 二 )WPA_SUPPLICANT——Linux下的wifi管理工具移植-Go语言中文社区...
  10. mysql 添加索引 创建索引