本节书摘来自异步社区出版社《Imperfect C++中文版》一书中的第2章,第2.5节,作者: 【美】Martin D.Carroll , Margaret A.Ellis,更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.5 浅拷贝和深拷贝

C++代码设计与重用
2.5 浅拷贝和深拷贝
有两个操作,尽管它们具有某些不合乎需要的特性,但因为它们的使用范围很广,进而博得一定的注意,所以这两个操作在这里有必要特别提及一下,这两个操作就是浅拷贝操作和深拷贝操作。x对象的浅拷贝是指:另一个和x相同类型的,并且它的数据成员和x相对应的数据成员具有相同值的对象。x对象的深拷贝是指:另一个和x类型相同的对象,它具有x直接或间接指向的对象的一份拷贝,并且在拷贝里,所有共享和循环的联系依旧保留。考虑下面3个类:

class Z {//没有数据成员//...
};
class Y {
private::Z* z;//没有其他数据成员//...
};
class x {
private:int iY* y1;Y* y2;//没有其他数据成员//...
};

在图2.1中,x2是X类型对象x1的浅拷贝,x3是x1的深拷贝。

但是,对一个设计得很好,并且正确实现的程序库(几乎没有异常产生),它的用户应该可以请求创建某个对象的拷贝—通过拷贝构造函数—并且由程序库适当实现某种对象类型的拷贝。除了一些特殊的类之外,用户是不需要了解拷贝函数的实现机制的,也不应该指定用某种特殊的方式来拷贝一个对象。

让我们还是回到讨论的话题,对一个给定的类,我们很少用浅拷贝操作或深拷贝操作来实现它的拷贝构造函数。假设我们用下面代码来实现2.1节中的Rational类:

class Rational { private:Rational_rep* rep;//...};class Rational_rep {private:int num;int denom;//...};

在这里,我们只是简单地把成员变量num和denom移到一个单独的类里面。(我们将在8.2.4节看到,这种实现方法为类Rational的用户提供了链接兼容性。)下面是类Rational和类Rational_rep用深拷贝来实现的拷贝构造函数:

class Rational {public:Rational(const Rational& r) {Rep = new Rational_rep(*r.rep);}//...};class Rational_rep {public:Rational_rep(Rational_rep& rep) :num(rep.num),denom(rep.denom) {}//...);

当用浅拷贝或者深拷贝来正确实现拷贝构造函数的时候,我们应该理所当然地类似(浅拷贝还有很大区别)上面那样来实现这个构造函数。但是,上面代码之所以可以实现,也仅仅是某种巧合;我们并不向用户建议这种巧合。如果类的实现改变了,那么拷贝构造函数就可能不再实现浅拷贝或者深拷贝了。实际上,用户也不应该委托拷贝构造函数实现这两种拷贝。此外,这种(巧合)现象—指通过浅拷贝或者深拷贝来实现类的拷贝构造函数—发生的概率要比程序员所认为的少很多。因为对大部分类来说,浅拷贝和深拷贝都不能用来实现类的拷贝构造函数;并且对一些特殊的类,浅拷贝和深拷贝往往带有不适合需要的属性:它们不能保持程序的不变性。例如,为了尽可能地共享类Rational_rep,我们可以这样来改变类Rational的实现:

Struct Rational_rep {int refcnt;    //引用计数。//...};class Rational {public:Rational(const Rational& r) {red = r.rep;++rep->refcnt;}//...};

一般当我们要共享某个对象的时候,我们必须增加引用计数,用它来决定在什么时候可以删除一个共享对象。对于任何使用类Rational这个版本的程序,它的不变性是指类Rational_rep里面的引用计数等于指向这个共享Rational_rep的类Rational的数目。读者容易看出,创建类Rational的浅拷贝将会违背这个不变性。

类Rational的问题也并不局限于浅拷贝。考虑下面的转型,它在Rational不为零的情况下返回真值。

class Rational {public:operator bool() const {return rep->num != 0;}//...};

假设用户经常调用这个函数。为了优化这个函数的实现,让我们改变类Rational的实现,来使所有值为零的Rational对象都指向同一个Rational_rep对象:

class Rational {public:operator bool() const {return rep == rep_of_zero;}//...private:static Rational_rep* rep_of_zero;//...};

这个bool转型函数避免了一个间接的调用(很显然获得了一些效率的优化,但这仅仅是一个例子)。读者容易核实,创建一个值为零的Rational对象的深拷贝,将会破坏不变性,这里的不变性是指,所有值为零的Rational对象都指向同一个Rational_rep对象1。

如果浅拷贝或者深拷贝操作有可能破坏某个不变性定义的话,那么类的实现者当然可以通过插入代码以恢复这个不变性,从而避免破坏。然而,保存不变性这个做法并没有改变浅拷贝或深拷贝破坏不变性的这个事实。况且,在更加复杂的类里面,浅拷贝或者深拷贝操作也照样破坏不变性,并且这种破坏性是很难甚至不可能得到修复的(见练习2.5c)。

假设类X的浅拷贝和深拷贝操作不会破坏任何不变性,那么类X是否应该提供这些操作呢?见下面例子:

class X {public:X* shallow_copy();  //应该提供这个函数吗?X* deep_copy();   //应该提供这个函数吗?//...};

当然不应该,除非X是那些非常特殊的类,并且允许用户指定它的拷贝实现方式。另一方面,如果我们想改变类X的实现,又不得破坏不变性,并且提供浅拷贝和深拷贝操作,那么这种改变也是不可能实现的。因此,我们可以这样认为:类一般不应该提供浅拷贝和深拷贝操作(见练习2.5d)。

1译注:由深拷贝的定义可知,如果执行深拷贝操作,那么也将拷贝出一个新的Rational rep对象,这与要求只有同一个Rational rep对象矛盾。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。

《C++代码设计与重用》——2.5 浅拷贝和深拷贝相关推荐

  1. 《C++代码设计与重用》——1.2 重用的神话

    本节书摘来自异步社区出版社<Imperfect C++中文版>一书中的第1章,第1.2节,作者: [美]Martin D.Carroll , Margaret A.Ellis,更多章节内容 ...

  2. 《C++代码设计与重用》——1.7 参考文献和相关资料

    本节书摘来自异步社区出版社<Imperfect C++中文版>一书中的第1章,第1.7节,作者: [美]Martin D.Carroll , Margaret A.Ellis,更多章节内容 ...

  3. 从代码设计到应用开发,入坑深度学习看这本书就够了

    深度学习(Deep Learning)是机器学习中一种基于对数据进行表征学习的方法.近年来,深度学习已经在科技界.工业界日益广泛地应用.随着全球各领域多样化数据的极速积累和计算资源的成熟化商业服务,深 ...

  4. FPGA之道(71)提高设计的综合性能(三)提高设计的重用性与易改性

    文章目录 前言 提高设计的重用性 构建自己的IP库 提高设计的易改性 常量参数化模块设计 结构参数化模块设计 总线参数化 规模参数化 功能参数化 参数化设计的参数管理与组织 参数相关性 可传递的模块参 ...

  5. 多个常见代码设计缺陷

    0.前言 在软件设计开发中,代码的设计都体现在:子系统与子系统.模块与模块.函数与函数之间的关系,设计越糟糕的软件,维护成本越高,质量也往往难以达标和称赞. 好的设计必定是:层次关系简洁.清晰.易维护 ...

  6. js倒计时代码最简单的_代码设计开发-6大基本原则解读(最简单扼要的理解)

    前言 相信做过编程开发的都应该听说过设计模式,设计模式是历史上的编程大牛经过不断的探索,总结出来的一整套经验的总和.他们总结出来这23种设计模式,告诉我们编程按照这些编程的设计模式可以让我们代码的可重 ...

  7. 第三章-宝箱抽奖模块与代码设计(三)

    第三章-宝箱抽奖模块与代码设计(三) 简要 信息 作者 卡卡 博客 http://blog.csdn.net/kakashi8841 邮箱 john.cha@qq.com 本文所属专栏 http:// ...

  8. java设计缺陷_多个常见代码设计缺陷

    0.前言 在软件设计开发中,代码的设计都体现在:子系统与子系统.模块与模块.函数与函数之间的关系,设计越糟糕的软件,维护成本越高,质量也往往难以达标和称赞. 好的设计必定是:层次关系简洁.清晰.易维护 ...

  9. 改善代码设计 —— 简化条件表达式(Simplifying Conditional Expressions)

    系列博客 1. 改善代码设计 -- 优化函数的构成(Composing Methods) 2. 改善代码设计 -- 优化物件之间的特性(Moving Features Between Objects) ...

最新文章

  1. 字典推导式_聊一聊:python的各种推导式(列表推导式、字典推导式、集合推导式)...
  2. VIM 命令使用大全
  3. java lambda 应用场景,Java中Lambda的使用范围
  4. 奥托尼克斯接近开关型号_萨科微SLKOR的MOS 场效应管应用范围和型号
  5. 读取带空格字符串小结
  6. 音标与字母发音不同的字母总结
  7. SQLSERVER到底能识别多少个逻辑CPU?
  8. 如何使用jquery处理json数据
  9. plsql命令窗口使用
  10. matlab多径瑞利衰落信道,Matlab仿真多径信道瑞利衰落
  11. AI+IoT行业“飞轮效应”凸显,全球云服务能力将发挥关键作用
  12. 【刘润五分钟商学院】-163生存,还是灭亡,没有中间态
  13. opencv获取不规则图像
  14. java 简单考试系统 ——java程序设计
  15. 纸质的报销单错了就得重新来,可不可以填写电子报销单?
  16. flink 任务提交问题汇总
  17. Oracle分区表索引
  18. 【LaTex】IEEE论文作者信息排版
  19. 让iis支持二级域名泛解析
  20. 索尼Xperia XZ1 Compact刷机后的问题,电量一直锁定20%,手机卡无信号无服务,相机拍照成纯绿色图片

热门文章

  1. Big Data應用:以玩家意見之數據分析來探討何謂健康型線上遊戲(上)
  2. [zz]一份非常内行的Linux LVM HOWTO
  3. Infinispan 10.0.0.Beta2 和 9.4.8 发布,分布式集群缓存系统
  4. 推荐11个实用的JavaScript库
  5. python----1
  6. 【Eclipse中使用Git之一】把远程仓库的项目,clone到eclipse里面
  7. 面对 20 亿行代码,Google 如何管理?
  8. Android Studio下加入百度地图的使用 (一)——环境搭建
  9. 一句话评论设计模式六大原则--转
  10. stdarg.h(c标准库)