1441 字
7 分钟
智能指针
C++中的智能指针
智能指针是C++中用于管理动态内存的一种机制,它封装了原始指针,提供了自动的内存管理功能,从而帮助程序员避免内存泄漏、指针越界等常见问题。C++11引入了三种主要的智能指针类型:unique_ptr、shared_ptr和weak_ptr。
1. unique_ptr
unique_ptr是独占式的智能指针,它确保同一时间只有一个智能指针可以指向某个动态分配的对象。当unique_ptr被销毁时,它所管理的对象也会被自动销毁。 基本用法:
#include <memory>
int main() {
std::unique_ptr<int> ptr1(new int(10)); // 创建一个unique_ptr
// std::unique_ptr<int> ptr2 = ptr1; // 错误,不能复制unique_ptr
std::unique_ptr<int> ptr3 = std::move(ptr1); // 转移所有权
if (ptr1 == nullptr) {
// 此时ptr1不再拥有对象
}
// 使用ptr3
*ptr3 = 20;
return 0; // ptr3自动释放内存
}特点:
- 独占性:不能共享所管理的对象,不能进行复制操作,但可以转移所有权。
- 自动释放:当
unique_ptr离开作用域时,自动释放所管理的对象。 - 高效:由于独占性,不需要引用计数,效率较高。
2. shared_ptr
shared_ptr是共享式的智能指针,允许多个智能指针共享同一个动态分配的对象。它通过引用计数来管理对象的生命周期,当引用计数为0时,对象被自动销毁。 基本用法:
#include <memory>
int main() {
std::shared_ptr<int> ptr1(new int(10)); // 创建一个shared_ptr
std::shared_ptr<int> ptr2 = ptr1; // 复制shared_ptr,引用计数+1
{
std::shared_ptr<int> ptr3 = ptr1; // 引用计数+1
// 使用ptr3
} // ptr3离开作用域,引用计数-1
// 使用ptr1和ptr2
*ptr1 = 20;
return 0; // ptr1和ptr2离开作用域,引用计数为0,对象被销毁
}特点:
- 共享性:多个
shared_ptr可以共享同一个对象。 - 引用计数:内部维护一个引用计数,用于管理对象的生命周期。
- 自动释放:当引用计数为0时,自动释放对象。
3. weak_ptr
weak_ptr是一种弱引用智能指针,它不增加引用计数,主要用于解决shared_ptr可能引起的循环引用问题。 基本用法:
#include <memory>
int main() {
std::shared_ptr<int> ptr1(new int(10));
std::weak_ptr<int> weakPtr = ptr1; // 创建一个weak_ptr
if (std::shared_ptr<int> sharedPtr = weakPtr.lock()) { // 尝试提升为shared_ptr
// 对象存在,可以使用sharedPtr
} else {
// 对象已被销毁
}
return 0; // ptr1离开作用域,对象被销毁,weakPtr变为空
}特点:
- 弱引用:不增加引用计数,不会影响对象的生命周期。
- 提升:可以通过
lock()方法尝试提升为shared_ptr,如果对象存在,则提升成功,否则返回空shared_ptr。 - 解决循环引用:常用于解决
shared_ptr之间的循环引用问题。
智能指针的优势
- 自动管理内存:减少手动管理内存的需求,降低内存泄漏和指针错误的风险。
- 提高代码安全性:通过封装原始指针,提供更安全的操作方式。
- 简化代码:减少内存管理相关的代码,使代码更简洁易读。
注意事项
- 避免与原始指针混用:智能指针和原始指针混用可能导致未定义行为。
- 正确使用
new和delete:使用智能指针后,应避免手动调用new和delete。 - 理解循环引用:在使用
shared_ptr时,注意循环引用问题,必要时使用weak_ptr。 智能指针是C++现代编程中非常重要的特性,合理使用智能指针可以大大提高代码的健壮性和可维护性。
使用std::unique_ptr
场景:当你想要确保只有一个指针拥有对动态分配对象的访问权时。 示例:
#include <iostream>
#include <memory>
class Resource {
public:
void use() {
std::cout << "Using resource" << std::endl;
}
~Resource() {
std::cout << "Resource destroyed" << std::endl;
}
};
int main() {
// 创建一个unique_ptr管理Resource对象
std::unique_ptr<Resource> resPtr(new Resource());
// 使用资源
resPtr->use();
// 不需要手动删除资源,resPtr离开作用域时会自动删除
return 0;
}使用技巧:
- 使用
std::make_unique(C++14起可用)来创建unique_ptr,避免内存分配和构造函数调用之间的异常安全问题。auto resPtr = std::make_unique<Resource>();
- 在函数中返回
unique_ptr,以转移所有权。 - 在容器中存储
unique_ptr,以管理动态数组。
使用std::shared_ptr
场景:当你需要多个指针共享对同一个动态分配对象的访问权时。 示例:
#include <iostream>
#include <memory>
class Resource {
public:
void use() {
std::cout << "Using resource" << std::endl;
}
~Resource() {
std::cout << "Resource destroyed" << std::endl;
}
};
int main() {
// 创建shared_ptr
std::shared_ptr<Resource> sharedRes = std::make_shared<Resource>();
// 复制shared_ptr
std::shared_ptr<Resource> anotherSharedRes = sharedRes;
// 使用资源
sharedRes->use();
anotherSharedRes->use();
// 当所有shared_ptr都离开作用域时,资源会被自动删除
return 0;
}使用技巧:
- 使用
std::make_shared来创建shared_ptr,以提高效率。 - 注意避免循环引用,这会导致内存泄漏。
- 在需要共享资源的多线程环境中使用
shared_ptr。
使用std::weak_ptr
场景:用于解决std::shared_ptr的循环引用问题,或者当你需要观察某个对象是否存在而不影响其生命周期时。 示例:
#include <iostream>
#include <memory>
class Resource {
public:
void use() {
std::cout << "Using resource" << std::endl;
}
~Resource() {
std::cout << "Resource destroyed" << std::endl;
}
};
int main() {
std::shared_ptr<Resource> sharedRes = std::make_shared<Resource>();
std::weak_ptr<Resource> weakRes = sharedRes;
// 尝试提升weak_ptr到shared_ptr
if (std::shared_ptr<Resource> tempRes = weakRes.lock()) {
tempRes->use();
} else {
std::cout << "Resource is no longer available" << std::endl;
}
// 销毁shared_ptr
sharedRes.reset();
// 再次尝试提升
if (weakRes.expired()) {
std::cout << "Resource has been destroyed" << std::endl;
}
return 0;
}使用技巧:
- 使用
weak_ptr来打破shared_ptr的循环引用。 - 在需要观察对象是否存在时,使用
weak_ptr而不影响对象的生命周期。 - 使用
weak_ptr::lock来安全地访问对象,如果对象已被销毁,则返回一个空的shared_ptr。

