在 C++ 中,智能指针是管理动态分配内存的重要工具,它们可以自动释放内存,避免内存泄漏。以下是四种主要智能指针的详细介绍:
1. auto_ptr (已废弃)
基本特性
-
C++98 引入,C++17 中已移除
-
具有所有权转移语义
示例代码
cpp
#include <memory>
#include <iostream>
void autoPtrExample() {
std::auto_ptr<int> p1(new int(10));
std::cout << *p1 << std::endl; // 输出: 10
std::auto_ptr<int> p2 = p1; // 所有权转移
// std::cout << *p1 << std::endl; // 错误!p1 现在为空
std::cout << *p2 << std::endl; // 输出: 10
}
问题
-
所有权转移不明确,容易导致错误
-
不支持数组
-
不能在 STL 容器中使用
2. unique_ptr (C++11)
基本特性
-
独占所有权,不能拷贝,只能移动
-
轻量级,无额外开销
-
支持自定义删除器
-
支持数组
示例代码
cpp
#include <memory>
#include <iostream>
void uniquePtrExample() {
// 基本用法
std::unique_ptr<int> p1(new int(20));
std::cout << *p1 << std::endl; // 输出: 20
// 移动语义
std::unique_ptr<int> p2 = std::move(p1);
std::cout << *p2 << std::endl; // 输出: 20
// p1 现在为空
// 使用 make_unique (C++14)
auto p3 = std::make_unique<int>(30);
// 数组支持
auto p4 = std::make_unique<int[]>(5);
p4[0] = 1;
p4[1] = 2;
// 自定义删除器
auto deleter = [](int* p) {
std::cout << "Deleting int: " << *p << std::endl;
delete p;
};
std::unique_ptr<int, decltype(deleter)> p5(new int(50), deleter);
}
3. shared_ptr (C++11)
基本特性
-
共享所有权,使用引用计数
-
支持拷贝和赋值
-
线程安全(引用计数操作)
-
支持自定义删除器
示例代码
cpp
#include <memory>
#include <iostream>
class MyClass {
public:
MyClass(int val) : value(val) {
std::cout << "MyClass constructed: " << value << std::endl;
}
~MyClass() {
std::cout << "MyClass destroyed: " << value << std::endl;
}
void print() {
std::cout << "Value: " << value << std::endl;
}
private:
int value;
};
void sharedPtrExample() {
// 创建 shared_ptr
std::shared_ptr<MyClass> p1 = std::make_shared<MyClass>(100);
rd.xjyl.gov.cn/upload/1982074929729208321.html
rd.xjyl.gov.cn/upload/1982074929775345664.html
rd.xjyl.gov.cn/upload/1982074929792122880.html
rd.xjyl.gov.cn/upload/1982074929871814656.html
rd.xjyl.gov.cn/upload/1982074929980866560.html
rd.xjyl.gov.cn/upload/1982074930068946944.html
rd.xjyl.gov.cn/upload/1982074930337382400.html
rd.xjyl.gov.cn/upload/1982074930408685568.html
rd.xjyl.gov.cn/upload/1982074930429657088.html
rd.xjyl.gov.cn/upload/1982074930551291904.html
rd.xjyl.gov.cn/upload/1982074930614206464.html
rd.xjyl.gov.cn/upload/1982074930635177984.html
rd.xjyl.gov.cn/upload/1982074930706481152.html
rd.xjyl.gov.cn/upload/1982074930719064064.html
rd.xjyl.gov.cn/upload/1982074931075579904.html
rd.xjyl.gov.cn/upload/1982074931146883072.html
rd.xjyl.gov.cn/upload/1982074931247546368.html
rd.xjyl.gov.cn/upload/1982074931247546369.html
rd.xjyl.gov.cn/upload/1982074931373375488.html
rd.xjyl.gov.cn/upload/1982074931369181185.html
rd.xjyl.gov.cn/upload/1982074931369181184.html
rd.xjyl.gov.cn/upload/1982074931570507776.html
rd.xjyl.gov.cn/upload/1982074931604062208.html
rd.xjyl.gov.cn/upload/1982074931725697024.html
{
std::shared_ptr<MyClass> p2 = p1; // 引用计数 +1
p2->print();
std::cout << "Use count: " << p1.use_count() << std::endl; // 输出: 2
} // p2 析构,引用计数 -1
std::cout << "Use count: " << p1.use_count() << std::endl; // 输出: 1
p1->print();
// 自定义删除器
std::shared_ptr<MyClass> p3(new MyClass(200),
[](MyClass* p) {
std::cout << "Custom deleter called" << std::endl;
delete p;
});
}
4. weak_ptr (C++11)
基本特性
-
不拥有对象所有权
-
解决 shared_ptr 循环引用问题
-
必须从 shared_ptr 创建
循环引用问题示例
cpp
#include <memory>
#include <iostream>
class Node {
public:
std::shared_ptr<Node> next;
std::shared_ptr<Node> prev;
~Node() {
std::cout << "Node destroyed" << std::endl;
}
};
void circularReferenceProblem() {
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2; // 循环引用
node2->prev = node1; // 内存泄漏!
// node1 和 node2 的引用计数永远不会为 0
}
使用 weak_ptr 解决循环引用
cpp
class NodeSafe {
public:
std::shared_ptr<NodeSafe> next;
std::weak_ptr<NodeSafe> prev; // 使用 weak_ptr
~NodeSafe() {
std::cout << "NodeSafe destroyed" << std::endl;
rd.xjyl.gov.cn/upload/1982074931901857792.html
rd.xjyl.gov.cn/upload/1982074931914440704.html
rd.xjyl.gov.cn/upload/1982074932023492608.html
rd.xjyl.gov.cn/upload/1982074932027686912.html
rd.xjyl.gov.cn/upload/1982074932086407168.html
rd.xjyl.gov.cn/upload/1982074932115767296.html
rd.xjyl.gov.cn/upload/1982074932203847680.html
rd.xjyl.gov.cn/upload/1982074932262567936.html
rd.xjyl.gov.cn/upload/1982074932291928064.html
rd.xjyl.gov.cn/upload/1982074932317093888.html
rd.xjyl.gov.cn/upload/1982074932392591360.html
rd.xjyl.gov.cn/upload/1982074932367425536.html
rd.xjyl.gov.cn/upload/1982074932392591361.html
rd.xjyl.gov.cn/upload/1982074932640055296.html
rd.xjyl.gov.cn/upload/1982074932661026816.html
rd.xjyl.gov.cn/upload/1982074932728135680.html
rd.xjyl.gov.cn/upload/1982074932954628096.html
rd.xjyl.gov.cn/upload/1982074932958822400.html
rd.xjyl.gov.cn/upload/1982074933034319872.html
rd.xjyl.gov.cn/upload/1982074933306949632.html
rd.xjyl.gov.cn/upload/1982074933361475584.html
rd.xjyl.gov.cn/upload/1982074933424390144.html
rd.xjyl.gov.cn/upload/1982074933432778752.html
rd.xjyl.gov.cn/upload/1982074933508276224.html
rd.xjyl.gov.cn/upload/1982074933596356608.html
rd.xjyl.gov.cn/upload/1982074933575385088.html
}
};
void weakPtrSolution() {
auto node1 = std::make_shared<NodeSafe>();
auto node2 = std::make_shared<NodeSafe>();
node1->next = node2;
node2->prev = node1; // 不会增加引用计数
// 可以正常析构,无内存泄漏
}
void weakPtrUsage() {
auto shared = std::make_shared<int>(42);
std::weak_ptr<int> weak = shared;
// 检查对象是否还存在
if (auto locked = weak.lock()) {
std::cout << "Object exists: " << *locked << std::endl;
} else {
std::cout << "Object has been destroyed" << std::endl;
}
shared.reset(); // 释放对象
if (auto locked = weak.lock()) {
std::cout << "Object exists: " << *locked << std::endl;
} else {
std::cout << "Object has been destroyed" << std::endl;
}
}
最佳实践总结
-
优先使用 unique_ptr
-
默认选择,性能最好
-
明确表达独占所有权
-
-
需要共享所有权时使用 shared_ptr
-
多个对象需要访问同一资源
-
注意避免循环引用
-
-
使用 weak_ptr 打破循环引用
-
观察者模式
-
缓存场景
-
-
使用 make_shared 和 make_unique
-
更安全,避免显式 new
-
更好的性能(make_shared)
-
-
避免使用 auto_ptr
- 已被废弃,使用 unique_ptr 替代
完整示例
cpp
#include <memory>
#include <iostream>
#include <vector>
int main() {
// unique_ptr 示例
std::unique_ptr<int> unique = std::make_unique<int>(10);
// shared_ptr 示例
std::shared_ptr<int> shared1 = std::make_shared<int>(20);
std::shared_ptr<int> shared2 = shared1;
// weak_ptr 示例
std::weak_ptr<int> weak = shared1;
// 在容器中使用智能指针
std::vector<std::shared_ptr<int>> vec;
vec.push_back(std::make_shared<int>(1));
vec.push_back(std::make_shared<int>(2));
vec.push_back(std::make_shared<int>(3));
for (const auto& ptr : vec) {
std::cout << *ptr << " ";
}
std::cout << std::endl;
return 0;
}
智能指针是现代 C++ 内存管理的核心工具,正确使用可以显著提高代码的安全性和可维护性。