浅拷贝、深拷贝

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

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. MySQL索引知识学习笔记

    目录 一.索引的概念 二.索引分类 三.索引用法 四 .索引架构简介 五.索引适用的情况 六.索引不适用的情况 继我的上篇博客:Oracle索引知识学习笔记,再记录一篇MySQL的索引知识学习笔记,本 ...

  2. git分支合并解决冲突

    git分支合并,解决冲突 1.手动解决冲突 手动解决冲突,需要使用编辑器,把所有文件中出现的冲突地方修改,然后再添加到暂存区再提交 >>>>>>brancha so ...

  3. 【mysql】You must reset your password using ALTER USER statement before executing this statement. 报错处理

    1.问题:登陆mysql以后,不管运行任何命令,总是提示这个 mysql> select user,authentication from mysql.user; ERROR 1820 (HY0 ...

  4. C# 通过反射调用 Func 委托

    C# 通过反射调用 Func 委托 Intro 最近我的 NPOI 扩展库增加了,自定义输出的功能,可以自定义一个 Func 委托来设置要导出的内容,详细介绍请查看 https://www.cnblo ...

  5. MySQL(9)---纪录一次实际开发过程中用到的复杂存储过程

    Mysql(9)---纪录一次实际开发过程中用到的复杂存储过程 为了尽可能的还原当时为什么需要用到存储过程,下面我写了个详细的文档,我们可以从需求文档出发来分析. 有关存储过程之前也写了两篇文章来做铺 ...

  6. 百度ai语音识别

    //语音识别功能 var APP_ID = "149**323"; var API_KEY = "N1Po****o6WPUeU8er"; var SECRET ...

  7. 敏捷软件开发_设计原则<三>

    敏捷软件开发_设计原则 单一职责原则(single responsibilities principle,SRP) 原理:一个类应该只有一个变化 分离职责:如果不耦合的职责那么很简单,如果两个职责耦合 ...

  8. 执行插件的替代方式:用JS调用操作

    关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复229或者20161028可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...

  9. 实验吧简单的SQL注入1,简单的SQL注入

    接上面一篇博客. 实验吧简单的sql注入1 题目连接   http://ctf5.shiyanbar.com/423/web/ 同样,直接输入 1加个但引号,结果下面有返回错误,            ...

  10. [20190530]ORACLE 18c - ALTER SEQUENCE RESTART.txt

    [20190530]ORACLE 18c - ALTER SEQUENCE RESTART.txt --//以前遇到要重置或者调整seq比较麻烦,我有时候采用比较粗暴的方式就是删除重建.--//18c ...