C++ 智能指针是 C++11 引入的自动化内存管理工具,用于替代裸指针,防止内存泄漏和悬空指针问题。以下是三种主要智能指针的详细说明及示例:
1. std::unique_ptr
:独占所有权
-
特点:
-
同一时间只能有一个
unique_ptr
指向对象。 -
对象生命周期与
unique_ptr
绑定,指针销毁时对象自动释放。 -
不可复制,但可通过
std::move
转移所有权。
-
-
适用场景:
-
明确资源唯一所有权的场景(如工厂模式返回对象)。
-
-
示例:
#include <iostream>
#include <memory>
using namespace std;
class Resource {
public:
Resource()
{
cout << "Resource Created" << endl;
}
~Resource()
{
cout << "Resource Destroyed" << endl;
}
void use()
{
cout << "Using Resource " << endl;
}
};
int main() {
// 创建 unique_ptr
unique_ptr<Resource> ptr1 = make_unique<Resource>();
ptr1->use(); // 输出: Using Resource
// 转移所有权
unique_ptr<Resource> ptr2 = move(ptr1);
if (!ptr1) {
// 输出: ptr1 is now empty
cout << "ptr1 is now empty" << endl;
}
else{
cout << "ptr1 is not empty" << endl;
}
// 测试指针ptr2
if (ptr2){
ptr2->use();
}
return 0;
}
编译以上程序并且测试结果如下:
(base) blctrl@blctrl-s2:~/CPP_Program$ ./sp
Resource Created
Using Resource
ptr1 is now empty
Using Resource
Resource Destroyed
2. std::shared_ptr
:共享所有权
-
特点:
-
多个
shared_ptr
可共享同一对象。 -
通过引用计数跟踪对象使用者,计数归零时释放对象。
-
支持拷贝和赋值。
-
-
适用场景:
-
多个组件需共享同一资源(如缓存系统、观察者模式)。
-
-
示例:
#include <iostream>
#include <memory>
using namespace std;
class Data {
public:
Data()
{
m_data = 0;
cout << "Data Created" << endl;;
}
Data(int data) : m_data(data)
{
cout << "Data Created: " << m_data << endl;;
}
~Data()
{
cout << "Data Destroyed" << endl;;
}
void printData()
{
cout << "Data: " << m_data << endl;
}
private:
nt m_data;
};
int main() {
// 创建 shared_ptr
shared_ptr<Data> ptr1 = make_shared<Data>(10); // 输出: Data Created:10
{
// 引用计数变为 2
shared_ptr<Data> ptr2 = ptr1;
// 输出: 2
cout << "Inside scope: Use count = " << ptr2.use_count() << "" << endl;;
ptr2->printData();
} // ptr2 销毁,引用计数变为 1
cout << "Outside scope: Use count = " << ptr1.use_count() << "" << endl;; // 输出: 1
ptr1->printData();
return 0; // ptr1 销毁,引用计数归零,输出: Data Destroyed
}
编译以上程序,并且进行测试:
(base) blctrl@blctrl-s2:~/CPP_Program$ ./sp2
Data Created: 10
Inside scope: Use count = 2
Data: 10
Outside scope: Use count = 1
Data: 10
Data Destroyed
3. std::weak_ptr
:弱引用
-
特点:
-
指向
shared_ptr
管理的对象,但不增加引用计数。 -
需通过
lock()
方法获取临时shared_ptr
访问对象。 -
用于解决
shared_ptr
的循环引用问题。
-
-
适用场景:
-
观察者模式、缓存系统中避免循环引用。
-
-
示例(解决循环引用):
#include <iostream>
#include <memory>
using namespace std;
class NodeA; // 前向声明
class NodeB; // 前向声明
class NodeA {
public:
shared_ptr<NodeB> b_ptr;
~NodeA() { cout << "NodeA Destroyed\n"; }
};
class NodeB {
public:
// 使用 weak_ptr 替代 shared_ptr
weak_ptr<NodeA> a_ptr;
~NodeB() { cout << "NodeB Destroyed\n"; }
};
int main() {
shared_ptr<NodeA> a = make_shared<NodeA>();
shared_ptr<NodeB> b = make_shared<NodeB>();
cout << "a:" << a.use_count() << endl;
cout << "b:" << b.use_count() << endl;
cout << "a->b_ptr:" << a->b_ptr.use_count() << endl;;
cout << "b->a_ptr:" << b->a_ptr.use_count() << endl;
cout << "-------------------------------" << endl;
a->b_ptr = b;
b->a_ptr = a;
cout << "a:" << a.use_count() << endl;
cout << "b:" << b.use_count() << endl;
cout << "a->b_ptr:" << a->b_ptr.use_count() << endl;
cout << "b->a_ptr:" << b->a_ptr.use_count() << endl;
// a 和 b 销毁时,引用计数正确归零
return 0; // 输出: NodeA Destroyed, NodeB Destroyed
}
编译以上代码,并且进行测试:
(base) blctrl@blctrl-s2:~/CPP_Program$ ./sp3
a:1
b:1
a->b_ptr:0
b->a_ptr:0
-------------------------------
a:1
b:2
a->b_ptr:2
b->a_ptr:1
NodeA Destroyed
NodeB Destroyed
对比表格
特性 | std::unique_ptr | std::shared_ptr | std::weak_ptr |
---|---|---|---|
所有权 | 独占 | 共享 | 无(弱引用) |
拷贝/赋值 | 禁止拷贝,允许移动 (move ) | 允许拷贝和赋值 | 允许拷贝和赋值 |
内存释放时机 | 指针销毁时 | 引用计数归零时 | 不参与计数,不影响释放时机 |
典型应用场景 | 工厂模式、独占资源 | 缓存、共享数据 | 打破循环引用 |
注意事项
1)优先使用 make_unique
和 make_shared
避免直接使用 new
,以提高性能和异常安全性:
// 推荐
auto ptr = std::make_shared<Data>();
// 不推荐
std::shared_ptr<Data> ptr(new Data);
2)避免循环引用
若两个 shared_ptr
互相指向对方,会导致内存泄漏,需用 weak_ptr
解耦。
3)不要混合使用智能指针和裸指针
错误的裸指针操作可能导致悬空指针或重复释放:
int* raw_ptr = new int(5);
std::shared_ptr<int> sp1(raw_ptr);
// 错误!会导致重复释放
std::shared_ptr<int> sp2(raw_ptr);
总结
-
unique_ptr
:简单高效,用于明确所有权的场景。 -
shared_ptr
:灵活共享,但需注意循环引用。 -
weak_ptr
:辅助shared_ptr
,解决循环引用问题。
正确使用智能指针可显著提升代码安全性,减少内存管理错误。