![](https://i-blog.csdnimg.cn/blog_migrate/2f496bdcdde1389bed9dc0386fd89baf.png)
对象的生命周期至关重要。 确定对象生命周期的错误可能导致资源(例如内存,fd)泄漏,因为所拥有的资源无法正确释放和回收以供将来使用。 当泄漏累积到一定水平时,它将使整个系统崩溃。
对象的生命周期也很复杂,因为一个对象的所有权可能会被不同的实体放弃,转让或共享给不同的实体,这些实体包括但不限于变量, 函数参数,模块,数据结构,容器和线程。 同样,必须由一位所有者在某个不确定的点上释放和回收资源。
没有实际的标准来确定对象的生命周期。 Java中使用的GC (垃圾收集),Objective-C中使用的ARC以及C ++中所有这些指针(ptrs)之类的工具都有其优缺点。 但是,本文不讨论优缺点,而是重点介绍C ++资源管理帮助程序类,智能指针, shared_ptr
, auto_ptr
和unique_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
要简要说明上面的代码:
-
SmartPointer
的生命周期不超过普通类的生命周期。 因此,超出(功能)范围的逻辑流将对其进行破坏。 -
SmartPointer
具有两个属性_pRes
和_refCount
,这两个属性都是从堆分配的。 因此,超出(功能)范围的逻辑流不会破坏它们; - 每次使用有效的
_pRes
(类型T
)构造SmartPointer
,_refCount
加1
; - 在我们的情况下,每次a被
SmartPointer
破坏时,逻辑流都会超出范围,_refCount
减1
; - 但是,销毁
SmartPointer
并不一定会导致_pRes
的销毁:
a)当_refCount
仍然大于0
, SmartPointer
只需减小_refCount
并打印
![](https://i-blog.csdnimg.cn/blog_migrate/cff1c9acb676ae744cd9b86cf5523c59.png)
b)仅当_refCount
减0
, SmartPointer
_pRes
破坏_pRes
引用的资源,并打印
![](https://i-blog.csdnimg.cn/blog_migrate/26b23f06ffadeccf2f4ac663c40a6e7f.png)
因此, 智能指针作为程序的不同部分所使用的句柄来跟踪和控制资源实例。 当所有句柄都销毁后,该资源将被视为“未使用”,并且也会被删除。 在本文的最后,我将展示一些在现实世界中体现智能指针的真实句柄。
该示例展示了线性程序中智能指针的用法,在实际情况下很少出现这种情况。 相反,如前所述,资源(即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 v
, l1()
不再释放资源。 这获得了更广泛的用法的唯一指针 。
Nb,我宁愿认为唯一指针是引入新动作语义的主要原因。 因为与此处获得的改进相比, 移动和右值实现了优化 不那么重要。
带回家
“我能理解这些东西,但是不确定第二天早上是否仍然记得它们。”
当然。 我会找到一些现实世界的同行来增强您的记忆力。
1) std::shared_ptr
就像视频游戏机的手柄。
![](https://i-blog.csdnimg.cn/blog_migrate/538cf843c0c1106ddeccdf29bdd8e7ba.png)
控制台(资源)由具有手柄的多个玩家“ 共享” ,即使只剩下一个玩家,游戏也应继续。 因此,仅当所有玩家停止玩游戏时,“游戏结束”。
2) std::unique_ptr
就像一个便携式游戏机。
![](https://i-blog.csdnimg.cn/blog_migrate/914a183e9e17c7bcfc54777a2e557ace.png)
一次一个球员,一个人应该“ 移动”它,让另一个人玩。 当LAST玩家停止游戏时,“游戏结束”。
3) std::auto_ptr
是一个
![](https://i-blog.csdnimg.cn/blog_migrate/485146268af90c82953c3e033ce4cf3a.png)
因为它不容易移动 d。
如果您喜欢此读物,请为它鼓掌,或者单击按钮。 谢谢,希望下次见。
最初发布于 holmeshe.me 。
From: https://hackernoon.com/smart-pointer-shared-ptr-automatic-pointer-and-unique-ptr-4fb12ff53914