C++深拷贝与浅拷贝

对于普通类型的对象来说,它们之间的复制是很简单的,例如:
int a=88;
int b=a;
而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。下面看一个类对象拷贝的简单例子。

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

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

当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:

  • 一个对象以值传递的方式传入函数体
  • 一个对象以值传递的方式从函数返回
  • 一个对象需要通过另外一个对象进行初始化

如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一 个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝位拷贝又称浅拷贝,后面将进行说明。

自定义拷贝构造函数是一种良好的编程风格,它可以阻止编译器形成默认的拷贝构造函数,提高源码效率。

浅拷贝和深拷贝

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

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

深拷贝和浅拷贝的定义可以简单理解成:如果一个类拥有资源(堆,或者是其它系统资源),当这个类的对象发生复制过程的时候,这个过程就可以叫做深拷贝,反之对象存在资源,但复制过程并未复制资源的情况视为浅拷贝。浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程序运行出错。

Test(Test &c_t)是自定义的拷贝构造函数,拷贝构造函数的名称必须与类名称一致,函数的形式参数是本类型的一个引用变量,且必须是引用。

当用一个已经初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用,如果你没有自定义拷贝构造函数的时候,系统将会提供给一个默认的拷贝构造函数来完成这个过程,上面代码的复制核心语句就是通过Test(Test &c_t)拷贝构造函数内的p1=c_t.p1;语句完成的。

Q:什么是浅拷贝(shallow copy)和深拷贝(deep copy)?

A: 浅拷贝就是成员数据之间的一一赋值:把值一一赋给要拷贝的值。但是可能会有这样的情况:对象还包含资源,这里的资源可以是堆资源,或者一个文件。。当值拷贝的时候,两个对象就有用共同的资源,同时对资源可以访问,这样就会出问题。深拷贝就是用来解决这样的问题的,它把资源也赋值一次,使对象拥有不同的资源,但资源的内容是一样的。对于堆资源来说,就是在开辟一片堆内存,把原来的内容拷贝。

如果你拷贝的对象中引用了某个外部的内容(比如分配在堆上的数据),那么在拷贝这个对象的时候,让新旧两个对象指向同一个外部的内容,就是浅拷贝;如果在拷贝这个对象的时候为新对象制作了外部对象的独立拷贝,就是深拷贝 。

引用和指针的语义是相似的,引用是不可改变的指针,指针是可以改变的引用。其实都是实现了引用语义。 深拷贝和浅拷贝的区别是在对象状态中包含其它对象的引用的时候,当拷贝一个对象时,如果需要拷贝这个对象引用的对象,则是深拷贝,否则是浅拷贝。

COW语义是”深拷贝”与”推迟计算”的组合,仍然是深拷贝,而非浅拷贝,因为拷贝之后的两个对象的数据在逻辑上是不相关的,只是内容相同。

Q:什么情况下使用浅拷贝什么时候使用深拷贝?

A: 无论深浅,都是需要的。当深拷贝发生时,通常表明存在着一个”聚合关系”,而浅拷贝发生时,通常表明存在着一个”相识关系”。 举个简单的例子: 当你实现一个Composite Pattern,你通常都会实现一个深拷贝(如果需要拷贝的话),很少有要求同的Composite共享Leaf的; 而当你实现一个Observer Pattern时,如果你需要拷贝Observer,你大概不会去拷贝Subject,这时就要实现个浅拷贝。 是深拷贝还是浅拷贝,并不是取决于时间效率、空间效率或是语言等等,而是取决于哪一个是逻辑上正确的。

Q:在C++中default constructor对对象进行的是怎样的拷贝动作?

A: 再纠正一个概念:default constructor是不负责拷贝动作的,我想你说的应该是指implicitly-declared copy constructor。它会调用所有直系基类的copy constructor和有成员的copy constructor,并且复制vtpr。如果一个类:

1:没有虚方法和虚基类

2:所有直系基类的copy constructor都是无代价的

3:所有成员的copy constructor都是无代价的 这时它的copy constructor是无代价的,相当于用memcpy实现。

判断它是深拷贝还是浅拷贝,还是要根据类的实现。比如如果它有一个用原生指针指针实现的对象引用,或是用boost::shared_ptr等引用分享所有权的智能指针实现的对象引用,则这个拷贝是浅拷贝;如果是用copy_ptr这种实现了深拷贝的智能指针实现的对象引用,就是深拷贝了。

Original: https://www.cnblogs.com/biyeymyhjob/archive/2012/11/02/2751257.html
Author: as_
Title: C++深拷贝与浅拷贝

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/535516/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • TechEmpower 21轮Web框架 性能评测 — C# 的性能 和 Rust、C++并驾齐驱

    自从2021年2月第20轮公布的测试以后,一年半后 的2022年7月19日 发布了 TechEmpower 21轮测试报告:Round 21 results – Tec…

    C++ 2023年5月29日
    082
  • c++ 解析yaml文件

    一直用c++操作 ini做配置文件,想换成 yaml,在全球最大的同性交友网站 github上搜索,看有没有开源的库,功夫不负有心人,找到了yaml-cpp,试着解析了一个 yam…

    C++ 2023年5月29日
    076
  • std::get<C++11多线程库~线程管理>(09):运行时决定线程数量

    1 #include 2 #include 3 #include 4 #include 5 #include 6 7 /* 8 * 话题1: 运行时决定线程的数量。 9 * 线程的…

    C++ 2023年5月29日
    050
  • JNI支持C++与C的区别

    C++的代码后缀是”.cpp” 在JNI.h 文件中有两套代码。一套是支持c的, 一套是支持JNI的。 JNI中针对C的代码是: C中调用方式: JNI中针…

    C++ 2023年5月29日
    081
  • NDK自带的c/c++库

    1.官方文档 https://developer.android.google.cn/ndk/guides/stable_apis https://developer.androi…

    C++ 2023年5月29日
    083
  • 使用VS2015进行C++开发的6个主要原因

    使用VS2015进行C++开发的6个主要原因 使用Visual Studio 2015进行C++开发 在今天的 Build 大会上,进行了”将你的 C++ 代码转移至 …

    C++ 2023年5月29日
    082
  • C++中如何精确地输出特定类型的位数

    precision()函数和fixed合用的方法。 其中,fixed表示使用一般的方法(不是科学计数法之类的)输出浮点数。precision是cout自定义的函数,用来设置小数输出…

    C++ 2023年5月29日
    063
  • C++:继承访问属性(public/protected/private)

    我不去想是否能够成功 既然选择了远方 便只顾风雨兼程 Original: https://www.cnblogs.com/adylee/p/11432895.htmlAuthor:…

    C++ 2023年5月29日
    085
  • C++内存管理

    [ 导语] 内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对C++的痛恨,但内…

    C++ 2023年5月29日
    067
  • C#与C++之间类型的对应

    Windows Data Type .NET Data Type BOOL, BOOLEAN Boolean or Int32 BSTR String BYTE Byte CHAR…

    C++ 2023年5月29日
    093
  • CLion之C++框架篇-优化框架,引入boost(三)

    背景 结合上一篇CLion之C++框架篇-优化框架,单元测试(二) ,继续进行框架优化!这一版优化引入一个我们日常经常使用的操作库Boost,估算使用频率在70%以上! Boost…

    C++ 2023年5月29日
    0108
  • 【转】C++11 新特性总结

    其他路径: CSDN: https://blog.csdn.net/wodehao0808 微信公众号:程序喵星人 更多资源和视频教程,QQ:1902686547 前言 转载请注明…

    C++ 2023年5月29日
    078
  • C++ 11 关键字:thread_local(转)

    thread_local 是 C++ 11 新引入的一种存储类型,它会影响变量的存储周期。 C++ 中有 4 种存储周期: 有且只有 thread_local 关键字修饰的变量具有…

    C++ 2023年5月29日
    052
  • libnode 0.4.0 发布,C++ 语言版的 Node.js

    libnode 0.4.0 支持 Windows ,提升了性能,libuv 更新到 0.10.17 版本,libj 更新到 0.8.2 版本。 libnode 是 C++ 语言版的…

    C++ 2023年5月29日
    083
  • C++实现的各种排序算法

    提起排序算法相信大家都不陌生,或许很多人已经把它们记得滚瓜烂熟,甚至随时可以写出来。最近在学习这一块, 索性就把各种内部排序算法总结归纳了一下: 1、 算法分类: 十种常见排序算法…

    C++ 2023年5月29日
    061
  • 【C++】第1章 在VS2015中用C++编写控制台应用程序

    分类:C++、VS2015 创建日期:2016-06-12 看到不少人至今还在用VC 6.0开发工具学习C++,其实VC 6.0开发工具早就被淘汰了。这里仅介绍学习C++时推荐使用…

    C++ 2023年5月29日
    077
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球