8 Smart Pointers¶
文本统计:约 469 个字 • 174 行代码
普通指针管理内存容易造成内存泄漏(比如用完忘记释放),C++提供了智能指针用于内存的管理。
智能指针使用了一种RAII(资源获取即初始化)技术对普通的指针进行了封装,使得智能指针实质上是一个对象,但是行为表现得像一个指针
下面是一个 unique_ptr
类似的实现,u_ptr
类
template<typename T>
class u_ptr{
public:
explicit u_ptr(T *p = nullptr) : ptr(p) {} //构造函数显示调用
u_ptr(const u_ptr&) = delete; // 禁止拷贝构造函数
u_ptr& operator=(const u_ptr&) = delete; // 禁止拷贝赋值
u_ptr(u_ptr&& other) noexcept: ptr(other.release()) {} // 移动构造函数
u_ptr& operator=(u_ptr&& other) noexcept { // 移动赋值运算符
if (this != &other) {
reset(other.release()); // 释放当前资源
}
return *this;
}
~u_ptr() {
delete ptr;
}
T* operator->() const{
return ptr;
}
T& operator*() const{
return *ptr;
}
T* get() const{
return ptr;
}
T* release() {
T *temp = ptr;
ptr = nullptr; // 释放所有权
return temp;
}
void reset(T *p = nullptr) {
delete ptr; // 释放原有资源
ptr = p; // 接管新资源
}
private:
T *ptr;
};
std::unique_ptr
- 独占所有权:
unique_ptr
表示对其所指向的对象具有唯一的所有权,这意味着在任何给定的时间点只能有一个unique_ptr
指向某个对象。 - 禁止复制:由于其独占所有权的特性,
unique_ptr
不能被复制构造或赋值操作(即你不能简单地将一个unique_ptr
赋值给另一个unique_ptr
)。 - 支持移动语义:可以通过移动构造函数和移动赋值操作来转移所有权。
unique_ptr<Resource> p1 (new Resource);
unique_ptr<Resource> p2(new Resource(7));
//p1 = p2; 这样多个智能指针管理同一个资源会导致错误,因为是 unique_ptr
p1 = move(p2); // 这里是将 p2 的所有权转移给 p1,p1的资源也释放掉了
关于 std::move
与 右值引用
下面是一个 shared_ptr
类似的 s_ptr
的实现
template<typename T>
class s_ptr{
private:
struct ControlBlock{
int refCount;
T *ptr;
ControlBlock(T *p) : refCount(1), ptr(p) {}
};
ControlBlock *controlBlock;
void add_shared(){
if (controlBlock){
++controlBlock->refCount;
}
}
void release_shared(){
if (controlBlock){
--controlBlock->refCount;
if (controlBlock->refCount == 0){
delete controlBlock->ptr; // 释放资源
delete controlBlock; // 释放控制块
}
}
}
public:
explicit s_ptr(T *p = nullptr) {
if (p)
controlBlock = new ControlBlock(p);
else
controlBlock = nullptr;
}
void reset(T *p = nullptr) {
s_ptr(p).swap(*this); // 交换两个智能指针
}
s_ptr(const s_ptr& other) : controlBlock(other.controlBlock) {
add_shared();
}
s_ptr& operator=(const s_ptr& other) {
s_ptr(other).swap(*this); // 交换两个智能指针
return *this;
// s_ptr(other)这是一个临时对象,调用了拷贝构造函数
// 交换后,临时对象会被销毁,减少引用计数
/*
if (this != &other) {
release();
controlBlock = other.controlBlock;
add_shared();
}
return *this;*/
}
s_ptr(s_ptr&& other) noexcept : controlBlock(other.controlBlock) {
other.controlBlock = nullptr;
}
s_ptr& operator=(s_ptr&& other) noexcept {
if (this != &other) {
release_shared();
controlBlock = other.controlBlock;
other.controlBlock = nullptr;
}
}
void swap(s_ptr& other) noexcept{
std::swap(controlBlock, other.controlBlock);
}
~s_ptr() {
release_shared();
}
T* operator->() const {
return controlBlock->ptr;
}
T& operator*() const {
return *(controlBlock->ptr);
}
T* get() const {
return controlBlock ? controlBlock->ptr : nullptr;
}
int use_count() const {
return controlBlock ? controlBlock->refCount : 0;
}
};
std::shared_ptr
非独占的指针,可以多个指针管理一个对象。shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的
- 可以用
use_count
函数来查看某个对象拥有的指针的个数 - 资源对象在所有管理者释放时自动释放资源
shared_ptr<Resource> p2(new Resource(7));
{
shared_ptr<Resource> p1(new Resource(1));
cout<< "p1:" << p1.get() << endl;
cout<< "p1 data: " << p1->data << endl;
cout<< "p1 use_count: " << p1.use_count() << endl;
//shared_ptr<Resource> p2 (p1); // 共享所有权
p2 = move(p1);
cout<< "p2:" << p2.get() << endl;
cout<< "p2 data: " << p2->data << endl;
cout<< "p2 use_count: " << p2.use_count() << endl;
}
//shared_ptr 在最后一个管理者释放时会自动释放资源
这样输出为
Resource acquired: 7
Resource acquired: 1
p1:0xf24680
p1 data: 1
p1 use_count: 1
Resource released: 7
p2:0xf24680
p2 data: 1
p2 use_count: 1
Resource released: 1
注意区分 p2 = p1
和 p2 = move(p1)
的区别,一种是增加管理者,另一种是管理权转移。改为 p2 = p1
输出即为: