C++:Copy & Reference Count
浅拷贝、深拷贝
通常,我们会按如下方式书写拷贝构造函数:
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
的时候,我们的构造函数只是简单的把l1
的lif
成员的值赋予了l2
的lif
,也就是说,它们保存的都是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的更多相关文章
- 错误代码: 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 ...
- Python的进阶:copy与deepcopy区别
copy()与deepcopy()之间的区分必须要涉及到python对于数据的存储方式. 首先直接上结论: —–我们寻常意义的复制就是深复制,即将被复制对象完全再复制一遍作为独立的新个体单独存在.所以 ...
- AJ整理问题之:copy,对象自定义copy 什么是property
AJ分享,必须精品 copy copy的正目的 copy 目的:建立一个副本,彼此修改,各不干扰 Copy(不可变)和MutableCopy(可变)针对Foundation框架的数据类型. 对于自定义 ...
- 调试:'Object reference note set to an instance of an object.'
今天调试代码遇到一个奇怪的问题,每次调试到 var files = new List<string>()这一行代码,总是报错:System.NullReferenceException: ...
- netty系列之:JVM中的Reference count原来netty中也有
目录 简介 ByteBuf和ReferenceCounted ByteBuf的基本使用 ByteBuf的回收 ByteBuf的衍生方法 ChannelHandler中的引用计数 内存泄露 总结 简介 ...
- sphinx :undefined reference to `libiconv' 报错解决办法
sphinx :undefined reference to `libiconv' 报错解决办法 2013-11-30 21:45:39 安装sphinx时不停报错...郁闷在make时报错,错误 ...
- ArcGIS AddIN异常之:object reference not set to an instance of an object
异常出现在 frmDownload frd = new frmDownload(); frd.ShowDialog(); 在ArcMap中能正常弹出窗体,点击按钮时显示此异常:object refer ...
- Xcode 7:Storyboard Reference、Strong IBOutlet以及Scene Dock
本文由CocoaChina译者小袋子(博客)翻译原文:Storyboard Reference, Strong IBOutlet, Scene Dock in iOS 9 在这个教程中,我想要聊一些有 ...
- dos文件(夹)复制命令:copy和xcopy
1.copy命令 将一份或多份文件复制到另一个位置. COPY [/D] [/V] [/N] [/Y | /-Y] [/Z] [/L] [/A | /B ] source [/A | /B] [+ s ...
随机推荐
- Python的条件锁与事件共享
1:事件机制共享队列: 利用消息机制在两个队列中,通过传递消息,实现可以控制的生产者消费者问题要求:readthread读时,writethread不能写:writethread写时,readthre ...
- Express中app.use()用法 详解
app.use(path,callback)中的callback既可以是router对象又可以是函数 app.get(path,callback)中的callback只能是函数 当一个路由有好多个子路 ...
- SQL Server内部如何管理对象的数据Page?
一个表或Index使用的数据页空间是由IAM Page Chain来管理的.SQL Server 使用一个IAM(Index Allocation Map)Page来管理数据库文件中最多4GB的空间, ...
- jxl解析excel时,处理中文乱码问题
jxl解析excel时,处理中文乱码问题 一般出现较多的问题是,当exce中包含了中文或特殊字符时,在解析时候就会出现乱码现象. 解决方法为: InputStream in = new FileInp ...
- java基础(17):包装类、System、Math、Arrays、大数据运算
1. 基本类型包装类 大家回想下,在第三篇文章中我们学习Java中的基本数据类型时,说Java中有8种基本的数据类型,可是这些数据是基本数据,想对其进行复杂操作,变的很难.怎么办呢? 1.1 基本类型 ...
- 用 Python 带你看各国 GDP 变迁
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 周萝卜 源自:萝卜大杂烩 PS:如有需要Python学习资料的小伙伴 ...
- 易优CMS:type的基础用法
[基础用法] 名称:type 功能:获取指定栏目信息 语法: {eyou:type typeid='栏目ID' empty='暂时没有数据'} <a href="{$field.typ ...
- vue学习笔记(三): 启动说明
1.启动页面:index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" ...
- mac安装linux
http://www.mamicode.com/info-detail-503881.html
- 如何使用 CODING 进行瀑布流式研发
你好,欢迎使用CODING!这份最佳实践将帮助你通过 CODING 更好地实践瀑布流式开发流程. 什么是瀑布流式研发 1970 年温斯顿·罗伊斯(Winston Royce)提出了著名的"瀑 ...