前言
本文对C++的shared_ptr,unique_ptr,weak_ptr智能指针的概念及目的,应用场景,常见方法进行总结;
一、智能指针概述
智能指针是C++11中的新标准特性,是对new/delete分配释放内存进一步封装的一个类,一个智能指针对象,除了指向动态内存地址,还封装了计数器(shared_ptr),可以自动的释放动态资源,避免资源泄漏及悬空指针问题;
二、三种智能指针应用场景及常见方法比较
1.shared_ptr
场景:当需要多个指针访问同一个共享资源股时,可以使用shared_ptr;
常见方法示例:
#include <iostream>
#include <memory>
class SharedObject {
public:
SharedObject() { std::cout << "SharedObject constructed\n"; }
~SharedObject() { std::cout << "SharedObject destructed\n"; }
void doSomething() { std::cout << "Doing something\n"; }
};
int main() {
// 使用 make_shared 创建 shared_ptr
std::shared_ptr<SharedObject> sharedPtr1 = std::make_shared<SharedObject>();
// 使用 new 创建 shared_ptr
std::shared_ptr<SharedObject> sharedPtr2(new SharedObject());
// get() 方法
SharedObject* rawPtr = sharedPtr1.get();
rawPtr->doSomething();
// use_count() 方法
std::cout << "Reference count: " << sharedPtr1.use_count() << std::endl;
// reset() 方法
sharedPtr2.reset(new SharedObject()); // 替换为新的 SharedObject
sharedPtr2.reset(); // 释放 SharedObject,sharedPtr2 变为空
// swap() 方法
std::shared_ptr<SharedObject> sharedPtr3;
sharedPtr1.swap(sharedPtr3); // 交换 sharedPtr1 和 sharedPtr3
// operator* 和 operator->
sharedPtr1->doSomething();
// operator=
sharedPtr3 = sharedPtr1; // 增加引用计数
return 0;
}
补充说明:
- make_shared:创建一个 shared_ptr 实例,并自动管理对象的生命周期。 get():返回一个指向管理对象的原始指针。
use_count():返回引用计数,即有多少个 shared_ptr 和 weak_ptr 共享同一个对象。 reset():重置
shared_ptr,释放当前管理的对象,并可选地构造一个新的对象。 swap():交换两个 shared_ptr 所管理的对象。
operator* 和 operator->:允许通过解引用和成员访问运算符来访问 shared_ptr 管理的对象。
operator=:赋值运算符,通过移动语义转移 shared_ptr 的所有权。
2.unique_ptr
场景:当需要指针访问独占的访问某个资源时,可以使用unique_ptr;
常见方法示例:
#include <memory>
#include <iostream>
class MyClass {
public:
MyClass() { std::cout << "MyClass constructed\n"; }
~MyClass() { std::cout << "MyClass destructed\n"; }
void doSomething() { std::cout << "Doing something\n"; }
};
int main() {
// 构造函数
std::unique_ptr<MyClass> uniquePtr1(new MyClass()); // 默认构造函数
std::unique_ptr<MyClass> uniquePtr2 = std::make_unique<MyClass>(); // 使用 make_unique 创建
// get() 方法
MyClass* rawPtr = uniquePtr1.get(); // 获取原始指针
rawPtr->doSomething();
// reset() 方法
uniquePtr1.reset(new MyClass()); // 释放旧对象,构造新对象
uniquePtr1.reset(); // 释放对象,unique_ptr 变为空
// release() 方法
MyClass* releasedPtr = uniquePtr2.release(); // 释放对象,返回原始指针,unique_ptr 变为空
delete releasedPtr; // 需要手动删除释放的指针
// swap() 方法
std::unique_ptr<MyClass> uniquePtr3(new MyClass());
uniquePtr1.swap(uniquePtr3); // 交换 uniquePtr1 和 uniquePtr3 所管理的对象
// operator* 和 operator->
uniquePtr2->doSomething(); // 访问 MyClass 的成员函数
// operator=(赋值运算符)
std::unique_ptr<MyClass> uniquePtr4;
uniquePtr4 = std::move(uniquePtr2); // 转移所有权,uniquePtr2 变为空
// 比较运算符
std::unique_ptr<MyClass> uniquePtr5(new MyClass());
if (uniquePtr5 == nullptr) {
std::cout << "uniquePtr5 is empty\n";
}
if (uniquePtr5 != nullptr) {
std::cout << "uniquePtr5 is not empty\n";
}
// 检查空值
if (!uniquePtr5) {
std::cout << "uniquePtr5 is empty\n";
}
return 0;
}
补充说明:
- 构造函数:使用 new 运算符或 std::make_unique 创建 unique_ptr。
get():返回一个指向管理对象的原始指针。 reset():重置 unique_ptr,释放当前管理的对象,并可选地构造一个新的对 象。
release():释放 unique_ptr 管理的对象,返回原始指针,并使 unique_ptr 变为空。释放的指针需要手动删除。
swap():交换两个 unique_ptr 所管理的对象。 operator* 和
operator->:允许通过解引用和成员访问运算符来访问 unique_ptr 管理的对象。
operator=(赋值运算符):通过移动语义转移 unique_ptr 的所有权。 比较运算符:可以检查 unique_ptr
是否为空。 空值检查:直接使用 if (!uniquePtr) 来检查 unique_ptr 是否为空。
3.weak_ptr
场景:但需要不增加引用计数的同时,范围共享资源时,可以使用weak_ptr
常见方法:
#include <iostream>
#include <memory>
class WeakObject {
public:
WeakObject() { std::cout << "WeakObject constructed\n"; }
~WeakObject() { std::cout << "WeakObject destructed\n"; }
void doSomething() { std::cout << "Doing something\n"; }
};
int main() {
// 创建 shared_ptr
std::shared_ptr<WeakObject> sharedPtr1 = std::make_shared<WeakObject>();
// 创建 weak_ptr
std::weak_ptr<WeakObject> weakPtr1(sharedPtr1);
// 使用 weak_ptr 的 lock() 方法获取 shared_ptr
std::shared_ptr<WeakObject> lockedPtr = weakPtr1.lock();
if (lockedPtr) {
lockedPtr->doSomething();
}
// 检查 weak_ptr 是否有效
if (weakPtr1.expired()) {
std::cout << "WeakObject has been deleted\n";
}
// reset() 方法
weakPtr1.reset(); // 重置 weak_ptr
// swap() 方法
std::weak_ptr<WeakObject> weakPtr2;
weakPtr1.swap(weakPtr2); // 交换 weakPtr1 和 weakPtr2
// 使用 weak_ptr 的 use_count() 方法
std::cout << "Weak reference count: " << weakPtr1.use_count() << std::endl;
return 0;
}
补充说明:
- lock():获取一个 shared_ptr,如果 weak_ptr 已经失效,则返回一个空的 shared_ptr。
expired():检查 weak_ptr 是否有效,即它所指向的对象是否已经被删除。 reset():重置
weak_ptr,使其不再指向任何对象。 swap():交换两个 weak_ptr。 use_count():返回引用计数,即有多少个
shared_ptr 共享同一个对象(注意:weak_ptr 本身不增加引用计数)。
总结
以上就是今天要分享的内容,下表是对三种指针指针的对比,至于更多的使用细节可以参考其他优秀博客。
特性/方法 | std::unique_ptr | std::shared_ptr | std::weak_ptr |
---|---|---|---|
所有权 | 独占所有权 | 共享所有权 | 弱引用,不拥有所有权 |
构造 | new 或 std::make_unique | new 或 std::make_shared | 从 shared_ptr 构造 |
析构 | 自动释放所管理的对象 | 自动释放所管理的对象(当引用计数为0时) | 不释放对象 |
复制 | 不可复制(移动语义) | 复制时增加引用计数 | 不可复制(移动语义) |
移动 | 可移动(转移所有权) | 可移动(转移所有权) | 可移动(转移弱引用) |
空值检查 | 使用 == nullptr | 使用 == nullptr | 使用 expired() 或 use_count() == 0 |
获取原始指针 | get() | get() | lock().get() |
重置 | reset() | reset() | reset() |
交换 | swap() | swap() | swap() |
引用计数 | 无 | 有(通过 use_count() 访问) | 无(通过 use_count() 访问共享计数) |
常用方法 | get() , release() , reset() , swap() | get() , reset() , swap() , use_count() | lock() , reset() , swap() , use_count() |
适用场景 | 需要独占访问资源时 | 需要多个所有者共享资源时 | 需要避免循环引用或延迟获取资源时 |