浅拷贝、深拷贝

通常,我们会按如下方式书写拷贝构造函数:

class LiF {
public:
LiF(int _lif = 0) : lif(_lif) {} // 默认构造函数
LiF(const LiF& l) : lif(l.lif) {} // 拷贝构造函数
private:
int lif;
};

这是正确的。但是,如果数据成员包含指针类型的话,这种写法就很危险了。

class LiF {
public:
LiF() { lif = new int(0); } // 为lif动态分配内存
LiF(const LiF& l) : lif(l.lif) {} // 拷贝构造函数
~LiF() { // 析构函数
delete lif; // 释放分配给lif的资源
lif = nullptr; // 置空
}
private:
int* lif;
}; LiF l1;
LiF l2(l1); // 程序结束析构l2时,程序将崩溃

在拷贝l1生成l2的时候,我们的构造函数只是简单的把l1lif成员的值赋予了l2lif,也就是说,它们保存的都是l1构造时分配的地址,当两者之中的某个对象被销毁时,构造函数正常执行,资源被释放,但之后如果另一个对象也被析构,lif的资源就会被重复释放,lif也就变成野指针。这种拷贝方式也称为浅拷贝,即只拷贝空间,不拷贝资源。

为了防止指针类型的数据成员出现野指针错误,对应地便有了深拷贝操作,即在拷贝对象内容的同时为拷贝的内容分配新的资源。

class LiF {
public:
LiF() { lif = new int(0); } // 为lif动态分配内存
LiF(const LiF& l) : lif(new int(*l.lif)) {} // 深拷贝构造函数
~LiF() { // 析构函数
delete lif; // 释放分配给lif的资源
lif = nullptr; // 置空
}
private:
int* lif;
}; LiF l1;
LiF l2(l1);

注意到,在上面的拷贝构造函数中,我们为新对象的lif成员分配了一块新的内存,即完成了深拷贝。

类的行为

从上面的例子可以得到两种抽象的类行为:行为像值行为像指针

行为像值的类

即类提供的构造函数是深拷贝,类的每个对象都有自己的一份拷贝。对于这样的类,它显然需要:一个深拷贝构造函数、一个深拷贝赋值运算符重载、一个可以释放成员占用的资源的析构函数。

class LiF {
public:
LiF(const int& _lif = 0): lif(new int(_lif)) {}
LiF(const LiF& l) : lif(new int(*l.lif)) {}
LiF& operator= (const LiF& l) {
lif = new int(*l.lif);
return *this;
}
~LiF() {
delete lif;
lif = nullptr;
}
private:
int* lif;
};

行为像指针的类

即类提供的是浅拷贝,但由于可能有多个对象成员值相同一段内存,所以我们不能在析构时简单地释放资源。为了解决浅拷贝带来的野指针问题,需要引入一种技术——引用计数(reference count)。这也是C++11的智能指针shared_ptr的实现。

引用计数:

  • 在每个构造函数初始化对象时,额外创建一个引用计数并置为1,用以记录有多少对象正在共享资源。
  • 在执行拷贝操作时进行浅拷贝,同时拷贝计数器,并递增计数器,指出共享的对象增加了一个。
  • 在进行拷贝赋值时比较特殊但也很容易理解:需要递增右侧对象的计数器并递减左侧对象的计数器。若左侧对象引用计数归零,则释放资源。
  • 在析构对象时,并不直接释放共享的资源,而是递减计数器,直至计数器归零才释放资源。
class LiF {
public:
LiF(const int& _lif = 0): lif(new int(_lif)), referenceCount(new unsigned(1)) {} LiF(const LiF& l) :
lif(l.lif), referenceCount(l.referenceCount) {
++ *referenceCount;
} LiF& operator= (const LiF& l) {
++ *l.referenceCount;
if (-- *referenceCount == 0) {
delete lif;
delete referenceCount;
}
lif = l.lif;
referenceCount = l.referenceCount;
return *this;
} ~LiF() {
if (-- *referenceCount == 0) {
delete lif;
delete referenceCount;
}
}
private:
int *lif;
unsigned *referenceCount;
};

C++:Copy & Reference Count的更多相关文章

  1. 错误代码: 1247 Reference 'startTime' not supported (forward reference in item list)

    1.错误描述 1 queries executed, 0 success, 1 errors, 0 warnings 查询:SELECT a.createUserId AS typeId, (SELE ...

  2. Python的进阶:copy与deepcopy区别

    copy()与deepcopy()之间的区分必须要涉及到python对于数据的存储方式. 首先直接上结论: —–我们寻常意义的复制就是深复制,即将被复制对象完全再复制一遍作为独立的新个体单独存在.所以 ...

  3. AJ整理问题之:copy,对象自定义copy 什么是property

    AJ分享,必须精品 copy copy的正目的 copy 目的:建立一个副本,彼此修改,各不干扰 Copy(不可变)和MutableCopy(可变)针对Foundation框架的数据类型. 对于自定义 ...

  4. 调试:'Object reference note set to an instance of an object.'

    今天调试代码遇到一个奇怪的问题,每次调试到 var files = new List<string>()这一行代码,总是报错:System.NullReferenceException: ...

  5. netty系列之:JVM中的Reference count原来netty中也有

    目录 简介 ByteBuf和ReferenceCounted ByteBuf的基本使用 ByteBuf的回收 ByteBuf的衍生方法 ChannelHandler中的引用计数 内存泄露 总结 简介 ...

  6. sphinx :undefined reference to `libiconv' 报错解决办法

    sphinx :undefined reference to `libiconv' 报错解决办法   2013-11-30 21:45:39 安装sphinx时不停报错...郁闷在make时报错,错误 ...

  7. ArcGIS AddIN异常之:object reference not set to an instance of an object

    异常出现在 frmDownload frd = new frmDownload(); frd.ShowDialog(); 在ArcMap中能正常弹出窗体,点击按钮时显示此异常:object refer ...

  8. Xcode 7:Storyboard Reference、Strong IBOutlet以及Scene Dock

    本文由CocoaChina译者小袋子(博客)翻译原文:Storyboard Reference, Strong IBOutlet, Scene Dock in iOS 9 在这个教程中,我想要聊一些有 ...

  9. dos文件(夹)复制命令:copy和xcopy

    1.copy命令 将一份或多份文件复制到另一个位置. COPY [/D] [/V] [/N] [/Y | /-Y] [/Z] [/L] [/A | /B ] source [/A | /B] [+ s ...

随机推荐

  1. js获取计算机操作系统版本

    如题,想要获取当先计算机的操作系统和版本号的话,可以用如下方法. 首先,创建osversion.js文件,文件里面的代码如下 var osData = [ { name: 'Windows 2000' ...

  2. 知识图谱如何运用于RecomSys

    将知识图谱作为辅助信息引入到推荐系统中可以有效地解决传统推荐系统存在的稀疏性和冷启动问题,近几年有很多研究人员在做相关的工作.目前,将知识图谱特征学习应用到推荐系统中主要通过三种方式——依次学习.联合 ...

  3. Elastic:用Docker部署Elastic栈

    安装 因为我们需要使用docker来进行安装,我们必须安装: docker:根据不同的操作系统,请按照要求安装docker.可以到网站https://docs.docker.com/去安装 docke ...

  4. Zabbix 系列文章

    Zabbix 所有文章目录 本系列文章都基于 Zabbix 4.0 , Zabbix 4.0 是属于长期支持版本,其它的长期支持版本有 3.0 .2.2 . 官方已经有中文文档, 官方文档链接: Za ...

  5. JS常用标签

    1.由来 JavaScript的出现就是为了解决,不需要将所有的表单数据全部提交到服务器. 2.添加 加载Js代码的三种方式: 第一种:<script></script>标签里 ...

  6. Java 线程与多线程

    Java是一门支持多线程的编程语言! 什么是进程? 计算机中内存.处理器.IO等资源操作都要为进程进行服务. 一个进程上可以创建多个线程,线程比进程更快的处理单元,而且所占用的资源也小,多线程的应用也 ...

  7. jenkins在搭建中常见的问题

    1.window环境读取不到chrom.exe System.setProperty("webdriver.chrome.driver","chromedriver.ex ...

  8. RdKafka文档翻译

    函数string rd_kafka_err2str ( integer $err ) 将rdkafka错误代码转换为字符串 integer rd_kafka_errno2err ( integer $ ...

  9. python从入门到放弃--线程进阶

    # ### 死锁,递归锁,互斥锁 from threading import Thread,Lock import time noodle_lock = Lock() kuaizi_lock = Lo ...

  10. shell脚本一次性将tab制表符改为4空格的方法

    问题描述: 今天需要修改一些bash脚本,因为考虑到pycharm里面能够直接写,而我用pycharm比较多,所以直接用pycharm写了,由于改的那个bash脚本是别的同事写的,里面的缩进都是用的T ...