C++智能指针通过RAII机制自动管理内存,解决了内存泄漏、野指针、重复释放和异常安全等问题。std::unique_ptr提供独占所有权,适用于单一所有者场景;std::shared_ptr通过引用计数实现共享所有权,适合多所有者共同管理资源;std::weak_ptr作为非拥有观察者,用于打破shared_ptr的循环引用。选择时应优先使用unique_ptr,需要共享时用shared_ptr,并配合weak_ptr避免循环引用。常见陷阱包括shared_ptr循环引用、裸指针混用导致多次释放、未使用make系列函数带来的性能与异常风险,以及自定义删除器缺失。最佳实践是默认选用unique_ptr,优先使用make_unique和make_shared,避免裸指针操作,明确资源所有权语义,并在必要时继承enable_shared_from_this以安全返回shared_ptr。

C++中的智能指针,本质上是RAII(Resource Acquisition Is Initialization)原则的完美体现,它通过对象生命周期来自动管理内存,从而有效避免了内存泄漏、野指针等困扰C++开发者多年的顽疾。简单来说,它们就是对原始指针的封装,让内存管理变得自动化且更安全。
要在C++中使用智能指针,你主要会和
std::unique_ptr
std::shared_ptr
std::weak_ptr
1. std::unique_ptr
unique_ptr
unique_ptr
unique_ptr
立即学习“C++免费学习笔记(深入)”;
#include <iostream>
#include <memory> // 包含智能指针头文件
class MyClass {
public:
MyClass() { std::cout << "MyClass 构造" << std::endl; }
~MyClass() { std::cout << "MyClass 析构" << std::endl; }
void doSomething() { std::cout << "MyClass doing something." << std::endl; }
};
void processUniquePtr() {
// 推荐使用 std::make_unique 创建 unique_ptr
std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();
ptr1->doSomething();
// unique_ptr 不能被复制,会报错:
// std::unique_ptr<MyClass> ptr2 = ptr1; // 编译错误
// 但可以被移动
std::unique_ptr<MyClass> ptr2 = std::move(ptr1);
if (ptr1 == nullptr) {
std::cout << "ptr1 已经被移动,现在为空。" << std::endl;
}
ptr2->doSomething();
// 当 ptr2 超出作用域时,MyClass 对象会被自动析构
} // ptr2 离开作用域,MyClass 对象析构
int main() {
processUniquePtr();
return 0;
}2. std::shared_ptr
shared_ptr
shared_ptr
shared_ptr
shared_ptr
#include <iostream>
#include <memory>
class AnotherClass {
public:
AnotherClass() { std::cout << "AnotherClass 构造" << std::endl; }
~AnotherClass() { std::cout << "AnotherClass 析构" << std::endl; }
void greet() { std::cout << "Hello from AnotherClass!" << std::endl; }
};
void processSharedPtr() {
// 推荐使用 std::make_shared 创建 shared_ptr,效率更高
std::shared_ptr<AnotherClass> s_ptr1 = std::make_shared<AnotherClass>();
s_ptr1->greet();
std::cout << "引用计数: " << s_ptr1.use_count() << std::endl; // 1
std::shared_ptr<AnotherClass> s_ptr2 = s_ptr1; // 复制,共享所有权
std::cout << "引用计数: " << s_ptr1.use_count() << std::endl; // 2
std::shared_ptr<AnotherClass> s_ptr3;
s_ptr3 = s_ptr1; // 再次复制
std::cout << "引用计数: " << s_ptr1.use_count() << std::endl; // 3
// 当 s_ptr2 离开作用域时,引用计数变为 2
// 当 s_ptr3 离开作用域时,引用计数变为 1
// 当 s_ptr1 离开作用域时,引用计数变为 0,AnotherClass 对象被析构
} // s_ptr1, s_ptr2, s_ptr3 离开作用域,AnotherClass 对象析构
int main() {
processSharedPtr();
return 0;
}3. std::weak_ptr
weak_ptr
shared_ptr
shared_ptr
shared_ptr
lock()
shared_ptr
lock()
shared_ptr
#include <iostream>
#include <memory>
class B; // 前向声明
class A {
public:
std::shared_ptr<B> b_ptr;
A() { std::cout << "A 构造" << std::endl; }
~A() { std::cout << "A 析构" << std::endl; }
};
class B {
public:
// 如果这里是 shared_ptr<A> a_ptr,就会形成循环引用
std::weak_ptr<A> a_ptr;
B() { std::cout << "B 构造" << std::endl; }
~B() { std::cout << "B 析构" << std::endl; }
};
void createCircularReference() {
std::shared_ptr<A> p_a = std::make_shared<A>();
std::shared_ptr<B> p_b = std::make_shared<B>();
p_a->b_ptr = p_b;
p_b->a_ptr = p_a; // 使用 weak_ptr 避免循环引用
// 此时,p_a 和 p_b 的引用计数都是 1。
// 如果 B::a_ptr 也是 shared_ptr,那么 A 和 B 都无法被析构。
// 但现在 B::a_ptr 是 weak_ptr,它不增加 A 的引用计数。
if (auto shared_a = p_b->a_ptr.lock()) {
std::cout << "B 仍然可以访问 A。" << std::endl;
}
} // p_a, p_b 离开作用域,A 和 B 对象会被正确析构
int main() {
createCircularReference();
return 0;
}在我看来,智能指针的出现,简直是C++内存管理领域的一场革命。它主要解决了传统C++裸指针(raw pointer)在内存管理中面临的几个核心难题,这些问题往往是导致程序不稳定、崩溃甚至安全漏洞的罪魁祸首。
首先,内存泄漏。这是最常见的问题,当你使用
new
delete
delete
delete
其次,野指针(dangling pointer)。当一块内存被
delete
unique_ptr
shared_ptr
unique_ptr
shared_ptr
shared_ptr
再者,重复释放(double free)。如果你不小心对同一块内存调用了两次
delete
unique_ptr
shared_ptr
最后,异常安全。在传统C++代码中,如果在
new
delete
delete
选择正确的智能指针,就像选择合适的工具来完成一项任务一样,是C++编程中一个非常实用的技能。它们各有侧重,理解这些才能避免“大炮打蚊子”或者“巧妇难为无米之炊”的窘境。
1. std::unique_ptr
适用场景:
unique_ptr
unique_ptr
unique_ptr
unique_ptr
如何选择: 只要资源不需要共享,就优先考虑unique_ptr
shared_ptr
2. std::shared_ptr
适用场景:
shared_ptr
shared_ptr
如何选择: 当你明确需要多个对象共同管理一个资源的生命周期时,选择
shared_ptr
shared_ptr
weak_ptr
3. std::weak_ptr
适用场景:
shared_ptr
weak_ptr
shared_ptr
weak_ptr
shared_ptr
weak_ptr
weak_ptr
weak_ptr
如何选择:
weak_ptr
shared_ptr
shared_ptr
weak_ptr
lock()
shared_ptr
总的来说,我的个人经验是:先从
unique_ptr
shared_ptr
shared_ptr
weak_ptr
尽管智能指针极大地简化了C++的内存管理,但它们并非万无一失。在我多年的编程实践中,也遇到过一些因为对智能指针理解不深而导致的“坑”。了解这些陷阱并遵循最佳实践,能让你的代码更加健壮。
常见的陷阱:
shared_ptr
shared_ptr
shared_ptr
// 错误示例:导致循环引用
struct Node {
std::shared_ptr<Node> next;
std::shared_ptr<Node> prev; // 如果这里也是 shared_ptr
~Node() { std::cout << "Node 析构" << std::endl; }
};
void bad_cycle() {
std::shared_ptr<Node> n1 = std::make_shared<Node>();
std::shared_ptr<Node> n2 = std::make_shared<Node>();
n1->next = n2;
n2->prev = n1; // 形成循环,n1和n2都不会被析构
} // 离开作用域,Node不会析构解决方案: 使用
std::weak_ptr
weak_ptr
// 正确示例:使用 weak_ptr
struct NodeFixed {
std::shared_ptr<NodeFixed> next;
std::weak_ptr<NodeFixed> prev; // 使用 weak_ptr
~NodeFixed() { std::cout << "NodeFixed 析构" << std::endl; }
};
void good_cycle() {
std::shared_ptr<NodeFixed> n1 = std::make_shared<NodeFixed>();
std::shared_ptr<NodeFixed> n2 = std::make_shared<NodeFixed>();
n1->next = n2;
n2->prev = n1; // n1的引用计数不会增加
} // 离开作用域,NodeFixed会被正确析构shared_ptr
shared_ptr
shared_ptr
shared_ptr
shared_ptr
// 错误示例:裸指针和 shared_ptr 混用导致多次释放 int* raw_ptr = new int(10); std::shared_ptr<int> s_ptr1(raw_ptr); // OK // std::shared_ptr<int> s_ptr2(raw_ptr); // 错误!会导致二次释放 // 正确的做法是 s_ptr2 = s_ptr1;
解决方案: 尽量避免从裸指针创建多个
shared_ptr
shared_ptr
shared_ptr
使用new
make_unique
make_shared
std::shared_ptr<T> p(new T())
std::unique_ptr<T> p(new T())
make_unique
make_shared
// 不推荐:两次内存分配,且可能存在异常安全问题 // std::shared_ptr<MyClass> ptr = std::shared_ptr<MyClass>(new MyClass());
解决方案: 总是优先使用
std::make_unique
std::make_shared
make_shared
new
shared_ptr
make_unique
new T()
std::shared_ptr<T>(...)
make_shared
make_unique
自定义删除器(deleter)的误用或遗漏: 如果智能指针管理的是非堆内存(如文件句柄、网络套接字等),或者需要特殊的释放逻辑,必须提供自定义删除器。忘记提供或提供错误的删除器会导致资源泄漏或崩溃。
// 示例:自定义文件删除器
void closeFile(FILE* fp) {
if (fp) {
fclose(fp);
std::cout << "文件已关闭。" << std::endl;
}
}
// std::unique_ptr<FILE, decltype(&closeFile)> file_ptr(fopen("test.txt", "w"), &closeFile);
// 如果没有 &closeFile,unique_ptr 会尝试用 delete 关闭文件,导致错误。解决方案: 当管理非标准堆内存或需要特殊清理逻辑的资源时,务必提供正确的自定义删除器。
最佳实践:
unique_ptr
unique_ptr
make_unique
make_shared
new
get()
shared_from_this
shared_ptr
shared_ptr
std::enable_shared_from_this<T>
shared_from_this()
shared_ptr
return std::shared_ptr<T>(this)
以上就是如何在C++中使用智能指针_C++智能指针使用核心指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号