智能指针,shared_ptr,自动指针和unique_ptr

对象的生命周期至关重要。 确定对象生命周期的错误可能导致资源(例如内存,fd)泄漏,因为所拥有的资源无法正确释放和回收以供将来使用。 当泄漏累积到一定水平时,它将使整个系统崩溃。
对象的生命周期也很复杂,因为一个对象的所有权可能会被不同的实体放弃,转让或共享给不同的实体,这些实体包括但不限于变量, 函数参数,模块,数据结构,容器和线程。 同样,必须由一位所有者在某个不确定的点上释放和回收资源。
没有实际的标准来确定对象的生命周期。 Java中使用的GC (垃圾收集),Objective-C中使用的ARC以及C ++中所有这些指针(ptrs)之类的工具都有其优缺点。 但是,本文不讨论优缺点,而是重点介绍C ++资源管理帮助程序类,智能指针, shared_ptrauto_ptrunique_ptr

智能指针

智能指针是普通指针的包装器类。 智能点使用参考计数定义生命周期,该参考计数反映了智能指针对象被参考多少次。

接下来,我将展示智能指针的简单实现。 该代码仅用于演示目的,因此没有健全性检查,没有异常处理和线程安全保证。

#include <stdio.h>

template < typename T > class SmartPointer {
private:
T* _pRes;
int* _refCount;
void _release() {
if(--(*_refCount) == 0) {
printf("---Valar Morghulis:%d\n",*_refCount);
delete _pRes;
delete _refCount;
} else {
printf("---not today:%d\n",*_refCount);
}
}
public:
SmartPointer() : _pRes(NULL), _refCount(NULL) {
_refCount = new int(0);
printf("SP default cons:%d\n",*_refCount);
}
SmartPointer(T* pRes) : _pRes(pRes), _refCount(NULL) {
_refCount = new int(1);
printf("SP cons:%d\n",*_refCount);
}
SmartPointer(const SmartPointer<T>& sp) : _pRes(sp._pRes), _refCount(sp._refCount) {
(*_refCount)++;
printf("SP copy cons:%d\n",*_refCount);
}
SmartPointer<T>& operator = (const SmartPointer<T>& sp) {
this->_release(); // release the last resource it points to
_pRes = sp._pRes;
_refCount = sp._refCount;
(*_refCount)++;
printf("SP assign:%d\n",*_refCount);
return *this;
}
~SmartPointer() {
this->_release();
}
// to mimic a real pointer
T& operator* () {
return *_pRes;
}
// to mimic a real pointer
T* operator-> () {
return _pRes;
}
};
class AClass {
public:
AClass() { printf("aclass cons\n"); }
~AClass() { printf("aclass des\n"); }
};
void l2(SmartPointer<AClass>& p) {
SmartPointer<AClass> use3 = p; // >> SP copy cons:3
} // >> ---not today:2
void l1(SmartPointer<AClass>& p) {
SmartPointer<AClass> use2 = p; // >> SP copy cons:2
l2(p);
} // >> ---not today:1
int main() {
AClass *res = new AClass(); // >> aclass cons
SmartPointer<AClass> aSmartP(res); // >> SP cons:1
l1(aSmartP);
} // >> ---Valar Morghulis:0
// >> aclass des

结果:

aclass cons
SP cons:1
SP copy cons:2
SP copy cons:3
---not today:2
---not today:1
---Valar Morghulis:0
aclass des

要简要说明上面的代码:

  1. SmartPointer的生命周期不超过普通类的生命周期。 因此,超出(功能)范围的逻辑流将对其进行破坏。
  2. SmartPointer具有两个属性_pRes_refCount ,这两个属性都是从堆分配的。 因此,超出(功能)范围的逻辑流不会破坏它们;
  3. 每次使用有效的_pRes (类型T )构造SmartPointer_refCount1
  4. 在我们的情况下,每次a被SmartPointer破坏时,逻辑流都会超出范围, _refCount1
  5. 但是,销毁SmartPointer并不一定会导致_pRes的销毁:

a)当_refCount仍然大于0SmartPointer只需减小_refCount并打印

今天不行

b)仅当_refCount0SmartPointer _pRes破坏_pRes引用的资源,并打印

人人都要死

因此, 智能指针作为程序的不同部分所使用的句柄来跟踪和控制资源实例。 当所有句柄都销毁后,该资源将被视为“未使用”,并且也会被删除。 在本文的最后,我将展示一些在现实世界中体现智能指针的真实句柄。

该示例展示了线性程序中智能指针的用法,在实际情况下很少出现这种情况。 相反,如前所述,资源(即AClass的实例)可以由多个数据结构和并行变量共享。

shared_ptr(C ++ 11)

shared_ptr智能指针标准实现,比上面列出的演示代码更强大。 而且它不会生成躲避日志。

自动指针

自动指针虽然看上去与智能指针相似,但完全不同。 这是一个方便的帮助程序类,它在逻辑流超出范围时破坏资源,以防万一程序员忘记了。 在某种程度上,它使指针(指的是在运行时动态分配的内存块)的工作方式类似于堆栈变量(在编译时静态分配的)。

例如,AutoPointer v1.0:

#include <stdio.h>
template < typename T > class AutoPointer {
private:
T* _pRes;

public:
AutoPointer() : _pRes(NULL) {}

AutoPointer(T* pRes) : _pRes(pRes) {}

AutoPointer(const AutoPointer<T>& ap) : _pRes(ap._pRes) {}

AutoPointer<T>& operator = (const AutoPointer<T>& ap) {
delete _pRes;
_pRes = ap._pRes;

return *this;
}

~AutoPointer() {
delete _pRes;
}

// to mimic a real pointer
T& operator* () {
return *_pRes;
}
// to mimic a real pointer
T* operator-> () {
return _pRes;
}
};

class AClass {
public:
AClass() {printf(“cons\n”);}
~AClass() {printf(“des\n”);}
int i;
};

void l1(AutoPointer<AClass>& p) {
AutoPointer<AClass> use2 = p;
}//the resource has already been deallocated here

int main() {
AClass *res = new AClass();
res->i = 5;
AutoPointer<AClass> use1(res);
l1(use1);
}// abort, repeat deallocating pointer

结果:

cons
des
des
autop(1148,0x7fff74eff000) malloc: *** error for object 0x7f9940c03240: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
[1] 1148 abort ./a.out

如上面的代码片段所示, 自动指针在内部像简化的智能指针一样在内部工作,该智能指针在不考虑引用计数的情况下释放资源(实际上,根本没有引用计数)。

coredump显示了自动指针的一个主要缺点:所有权不能转移(到l1() )。 结果,即使资源已在l1()取消分配, main()仍将自身视为自动指针的所有者,并且将指针再分配一次。

如何实现复制构造函数以及赋值运算符,以便可以正确转移所有权?

例如,AutoPointer v2.0:

......
AutoPointer(AutoPointer<T>& ap) : _pRes(ap._pRes) {
ap._pRes = NULL;
}

AutoPointer<T>& operator = (AutoPointer<T>& ap) {
delete _pRes;
_pRes = ap._pRes;
    ap._pRes = NULL;
return *this;
}
......

结果:

cons
des

一切似乎都很好。 但这是“修复一个错误导致另一个错误”的另一个示例。

新的问题是所有权转移和复制这两个语义是耦合的。 因此,它与某些库函数(例如std::sort )不兼容,该函数需要一个额外的副本(作为快速排序的枢纽),因为它破坏了仍在使用的前一个副本。 有关问题的详细说明,请参见此处 ,并感谢patatahooligan指出了原始实现中的错误。

std::auto_ptr自动指针std实现。 如上所述,它不是很有趣或有问题,因此现在不建议使用。 我们应该使用std::unique_ptr代替。

std :: unique_ptr(C ++ 11)

std::unique_ptr是在std的更换std::auto_ptr在C ++ 11。 使用新添加的rvalue和move语义 ,可以将unique_ptr的所有权安全地转移到另一个实体。 此外,为unique_ptr禁用了复制语义,以避免我们在AutoPointer v2.0中看到的歧义。 像自动指针一样, 指针的最后所有者负责释放。

#include <iostream>
#include <vector>
#include <memory>
#include <cstdio>
#include <fstream>
#include <cassert>

class AClass {
public:
AClass() {printf(“cons\n”);}
~AClass() {printf(“des\n”);}
int i;
};

std::vector< std::unique_ptr<AClass> > v;

void l1() {
std::unique_ptr<AClass> p1(new AClass()); // >> cons
p1->i = 1;
v.push_back(std::move(p1));
std::unique_ptr<AClass> p2(new AClass()); // >> cons
p2->i = 2;
v.push_back(std::move(p2));
} // p1 and p2 are not destructed here

int main() {
l1();
for(auto& p: v) printf(“%d\n”, p->i);
} // >> des
// >> des

结果:

cons
cons
1
2
des
des

如上面的代码片段所示, 唯一指针在不同所有者之间保留。 将所有权移到 vector vl1()不再释放资源。 这获得了更广泛的用法的唯一指针

Nb,我宁愿认为唯一指针是引入新动作语义的主要原因 因为与此处获得的改进相比移动右值实现优化 不那么重要。

带回家

“我能理解这些东西,但是不确定第二天早上是否仍然记得它们。”

当然。 我会找到一些现实世界的同行来增强您的记忆力。

1) std::shared_ptr就像视频游戏机的手柄。

控制台(资源)由具有手柄的多个玩家“ 共享” ,即使只剩下一个玩家,游戏也应继续。 因此,仅当所有玩家停止玩游戏时,“游戏结束”。

2) std::unique_ptr就像一个便携式游戏机。

一次一个球员,一个人应该“ 移动”它,让另一个人玩。 当LAST玩家停止游戏时,“游戏结束”。

3) std::auto_ptr是一个

因为它不容易移动 d。

如果您喜欢此读物,请为它鼓掌,或者单击按钮。 谢谢,希望下次见。

最初发布于 holmeshe.me

From: https://hackernoon.com/smart-pointer-shared-ptr-automatic-pointer-and-unique-ptr-4fb12ff53914

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值