目录
1. 智能指针简介
C++智能指针是C++标准库中提供的一种自动管理动态分配内存的模板类。它们通过封装裸指针(即直接使用new
和delete
分配的指针)来自动管理内存的生命周期,从而减少了内存泄漏和野指针的风险。下面我将对C++中常见的几种智能指针(std::unique_ptr
、std::shared_ptr
和std::weak_ptr
)进行详细解析。
2. std::unique_ptr
std::unique_ptr
是一种独占所有权的智能指针,它保证了在同一时间内只有一个unique_ptr
可以指向一个给定的对象。当unique_ptr
被销毁时(例如,离开作用域),它所指向的对象也会被自动删除。std::unique_ptr
不允许复制构造或复制赋值,但支持移动语义,允许通过移动构造和移动赋值来转移所有权。
2.1. 特点
- 独占所有权:不能复制,但可以通过移动语义转移所有权。
- 自定义删除器:可以指定一个自定义的删除函数或lambda表达式来替代默认的
delete
操作。 - 数组支持:
std::unique_ptr<T[]>
专门用于管理动态分配的数组,其析构函数会调用delete[]
来释放内存。
2.2. 使用示例
#include <memory>
int main() {
std::unique_ptr<int> ptr1(new int(10)); // 创建unique_ptr
// std::unique_ptr<int> ptr2 = ptr1; // 错误,不能复制
std::unique_ptr<int> ptr3 = std::move(ptr1); // 通过移动语义转移所有权
// 此时ptr1变为空指针,ptr3指向原对象
return 0;
}
3. std::shared_ptr
std::shared_ptr
实现了共享所有权的智能指针,允许多个shared_ptr
实例指向同一个对象。每个shared_ptr
都有一个关联的计数器(通常隐藏在内部的控制块中),用于跟踪有多少个shared_ptr
指向该对象。当最后一个指向该对象的shared_ptr
被销毁时,对象才会被删除。std::shared_ptr
支持复制构造和复制赋值,这允许所有权在多个shared_ptr
之间共享。
3.1. 特点
- 共享所有权:支持复制和赋值,所有权在多个
shared_ptr
之间共享。 - 自定义删除器:同
unique_ptr
,可以指定自定义删除函数。 - 循环引用问题:需要小心处理,可能会导致内存泄漏。
3.2. 使用示例
#include <memory>
int main() {
std::shared_ptr<int> ptr1(new int(10));
std::shared_ptr<int> ptr2 = ptr1; // 复制,共享所有权
// 当ptr1和ptr2都销毁时,对象才会被删除
return 0;
}
4. std::weak_ptr
std::weak_ptr
是一种不拥有所指向对象的智能指针,它用于解决std::shared_ptr
之间可能形成的循环引用问题。weak_ptr
可以与shared_ptr
互操作,但它不增加对象的共享所有权计数。但它不增加对象的共享所有权计数。当你想在不需要拥有对象的情况下观察对象时,std::weak_ptr
非常有用。它允许你获取一个指向对象的std::shared_ptr
(如果对象仍然存在),但如果不存在,则返回一个空的shared_ptr
。
4.1. 特点
- 不拥有对象:不增加对象的共享所有权计数。
- 避免循环引用:与
shared_ptr
配合使用,打破循环引用。 - 转换为
shared_ptr
:可以通过lock()
方法尝试获取一个指向对象的shared_ptr
(如果对象仍然存在)。
4.2. 使用示例
#include <memory>
#include <iostream>
class A;
class B;
class A {
public:
std::shared_ptr<B> bPtr;
~A() { std::cout << "A destroyed\n"; }
};
class B {
public:
std::weak_ptr<A> aPtr;
~B() { std::cout << "B destroyed\n"; }
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->bPtr = b;
b->aPtr = a;
// a和b都销毁时,由于使用了weak_ptr,没有循环引用问题
return 0;
}
在这个例子中,A
和B
类通过shared_ptr
和weak_ptr
相互引用,但没有形成循环引用,因为B
类中使用的是weak_ptr
来引用A
。因此,当a
和b
的shared_ptr
被销毁时,它们指向的对象也会被正确删除,不会造成内存泄漏。
5. 使用智能指针的注意事项
使用C++智能指针时,需要注意以下几个方面以确保程序的正确性和效率。
5.1. 选择合适的智能指针类型
std::unique_ptr
:适用于独占所有权的场景,确保同一时间内只有一个智能指针指向对象。它不支持复制构造和赋值操作,但支持移动语义。std::shared_ptr
:适用于共享所有权的场景,允许多个智能指针指向同一个对象。它通过引用计数来管理对象的生命周期,当最后一个shared_ptr
被销毁时,对象也会被自动删除。std::weak_ptr
:不拥有对象的所有权,通常与std::shared_ptr
配合使用,用于解决循环引用问题。它不会增加引用计数,但可以通过lock()
方法尝试获取一个指向对象的shared_ptr
(如果对象仍然存在)。
5.2. 避免循环引用
当使用std::shared_ptr
时,需要特别注意避免循环引用,即两个或多个对象相互持有对方的shared_ptr
,导致它们的引用计数无法归零,从而无法释放内存。为了避免循环引用,可以使用std::weak_ptr
来替代其中一个shared_ptr
。
5.3. 谨慎使用自定义删除器
虽然智能指针允许指定自定义删除器,但应谨慎使用。自定义删除器可能会引入额外的复杂性和错误风险。在大多数情况下,使用默认的delete
操作就足够了。
5.4. 注意智能指针的性能开销
智能指针相比裸指针具有额外的性能开销,因为它们需要维护额外的信息(如引用计数)。在性能敏感的场景中,应评估使用智能指针的必要性,并考虑可能的优化措施。
5.5. 避免与裸指针混合使用
尽量避免将智能指针与裸指针(即直接使用new
和delete
)混合使用,因为这可能会导致资源的重复释放或内存泄漏。如果确实需要使用裸指针访问智能指针所管理的对象,应确保在智能指针的生命周期内进行访问,并避免在智能指针销毁后继续使用裸指针。
5.6. 合理使用移动语义
对于std::unique_ptr
,可以利用其移动语义来转移所有权,从而避免不必要的复制和性能开销。
5.7. 注意智能指针的生命周期
智能指针的生命周期应与它所管理的对象的生命周期相匹配。如果智能指针的生命周期过短,可能会导致对象被提前删除;如果过长,则可能会浪费内存资源。
5.8. 优先使用std::make_unique
和std::make_shared
在C++14及更高版本中,推荐使用std::make_unique
和std::make_shared
来创建智能指针。这些函数能够更高效地分配内存,并减少代码量。
5.9. 避免重复释放
确保不要对同一个智能指针多次调用reset()
、release()
或delete
(对于unique_ptr
通过get()
获取的裸指针),这可能会导致未定义行为,如双重释放。
5.10. 理解和使用std::weak_ptr
当需要观察std::shared_ptr
管理的对象但不拥有它时,可以使用std::weak_ptr
。它不会增加引用计数,因此不会阻止对象的销毁。但是,在访问对象之前,应使用lock()
方法尝试获取一个std::shared_ptr
,以确保对象仍然存在。
智能指针是C++中管理动态内存的强大工具,它们能够显著减少内存泄漏和程序崩溃的风险。然而,像任何强大的工具一样,它们也需要谨慎使用。
标签:std,C++,智能,shared,unique,ptr,指针 From: https://blog.csdn.net/weixin_37800531/article/details/142028398