一、auto_ptr
auto_ptr
是 C++98 标准中引入的智能指针,用于自动管理动态分配的对象。然而,它在语义上有一些缺陷,因此在 C++11 标准中被弃用,并由 unique_ptr
取代。
问题与缺陷:
1、所有权模型:auto_ptr
采用了“独占所有权”模型,即一个 auto_ptr
独占其指向的对象。当你将一个 auto_ptr
赋值给另一个 auto_ptr
时,所有权会从原 auto_ptr
转移到新的 auto_ptr
,原 auto_ptr
会变成空。这种所有权转移行为常常导致意外的错误。
2、不支持数组:auto_ptr
只能用于指向单个对象,而不能用于指向对象数组。尝试使用 auto_ptr
管理动态分配的数组会导致 delete
而不是 delete[]
被调用,从而引发未定义行为。
3、与标准库容器的兼容性问题:由于 auto_ptr
的复制语义(所有权转移),它不能与标准库容器(如 std::vector
)安全地一起使用。当容器尝试复制或重新排列其元素时,可能会意外地删除或移动元素。
由于上述问题,C++11 标准中引入了 unique_ptr
,它提供了与 auto_ptr
类似的独占所有权特性,但解决了 auto_ptr
的缺陷。unique_ptr
支持动态数组和移动语义,并且可以与 STL 容器和算法完美配合。
二、unique_ptr
unique_ptr通过指针占用并独占一个对象,并在unique_ptr离开作用域时释放该对象。
主要特点:
1、唯一所有权:一个 unique_ptr
指向的对象只能被一个 unique_ptr
所拥有。
2、自动删除:当 unique_ptr 被销毁时(例如,离开作用域时),它所指向的对象也会被自动删除。
3、禁止复制:unique_ptr
不支持拷贝构造和赋值操作,以防止多个 unique_ptr
指向同一个对象。
4、支持移动:unique_ptr
支持移动构造函数和移动赋值操作符,允许将所有权从一个 unique_ptr
转移到另一个 unique_ptr
。
三、shared_ptr
与 std::unique_ptr
相比,它支持多个指针共享对同一对象的所有权,而不仅限于单一所有权。这种特性使得 std::shared_ptr
在需要多个所有者管理同一对象的情况下非常有用。
主要特点:
1、引用计数:std::shared_ptr
使用引用计数来跟踪有多少个 shared_ptr
指向同一个对象。每次创建或复制 shared_ptr
时,引用计数会增加;每次 shared_ptr
被销毁或重置时,引用计数会减少。当引用计数为零时,shared_ptr
会自动销毁对象并释放内存。
2、自动资源释放:std::shared_ptr
采用了RAII(Resource Acquisition Is Initialization)技术,它在对象生命周期结束时自动释放相关资源,无需手动调用 delete
。
3、共享所有权:std::shared_ptr
支持多个 shared_ptr
对象共享同一块内存资源。当所有的 shared_ptr
对象都被销毁或重置时,内存才会被释放。
4、可定制删除器:std::shared_ptr
允许指定一个删除器(deleter)函数来代替默认的 delete
操作,这样可以实现自定义的资源释放策略。
5、可拷贝:std::shared_ptr
可以被拷贝,每次拷贝都会增加引用计数。当最后一个 shared_ptr
被销毁时,内存会被释放。
6、异常安全:std::shared_ptr
的析构函数和删除器会在异常抛出时正常工作,避免资源泄露。
初始化方式:
std::shared_ptr
可以通过多种方式进行初始化,包括但不限于:
1、裸指针直接初始化:直接传入一个 new
表达式的结果给 shared_ptr
的构造函数。
2、拷贝构造:通过拷贝另一个 shared_ptr
对象来初始化新的 shared_ptr
对象。
3、移动构造:C++11 引入了移动语义,shared_ptr
也支持通过移动构造来初始化新的 shared_ptr
对象,这通常用于函数返回或传递所有权时。
4、std::make_shared
:这是一个工厂函数,用于更方便地创建 shared_ptr
对象。与直接使用 new
表达式相比,std::make_shared
可以更高效地分配内存,因为它可以在单次内存分配中同时分配对象和控制块。
常用成员函数:
std::shared_ptr
提供了一些常用的成员函数,以便进行各种操作:
get()
:返回shared_ptr
中保存的裸指针。
reset()
:不带参数时,若智能指针是唯一指向该对象的指针,则释放并置空;若智能指针不是唯一指向该对象的指针,则引用计数减少1,同时将智能指针置空。带参数时,若智能指针是唯一指向对象的指针,则释放并指向新的对象;若智能指针不是唯一的指针,则只减少引用计数,并指向新的对象。use_count()
:返回shared_ptr
的强引用计数。unique()
:若use_count()
为1,返回true
,否则返回false
。
注意事项:
1、尽量避免将一个裸指针传递给 std::shared_ptr
的构造函数,常用的替代手法是使用 std::make_shared
。
2、不要将 this
指针返回给 shared_ptr
。当希望将 this
指针托管给 shared_ptr
时,类需要继承自 std::enable_shared_from_this
,并且从 shared_from_this()
中获得 shared_ptr
指针。
3、不要使用相同的原始指针作为实参来创建多个 shared_ptr
对象,这会导致多重控制块的创建和对象的多次析构。
4、避免循环引用,即一组 shared_ptr 相互引用,可能会导致内存泄漏,因为它们的引用计数永远不会变为 0,从而无法释放内存。所以引入了 weak_ptr 来解决这个问题。
四、weak_ptr
weak_ptr 是一种弱引用指针,用于解决 shared_ptr 可能导致的循环引用问题。它可以观测到 shared_ptr 所管理的对象,但不会增加引用计数,不影响对象的生命周期。当最后一个 shared_ptr
被销毁时,即使还有 weak_ptr
指向该对象,对象也会被释放。
主要特点:
1、不增加引用计数:weak_ptr
指向一个 shared_ptr
管理的对象时,不会增加该对象的引用计数。
2、解决循环引用:在对象间存在相互引用时,使用 weak_ptr
可以避免循环引用导致的内存泄漏。
3、观察对象状态:通过 weak_ptr
,可以观察 shared_ptr
管理的对象的状态,如使用 use_count()
获取引用计数,使用 expired()
检查对象是否已被释放。
基本用法:
1、初始化:weak_ptr
通常由一个 shared_ptr
或另一个 weak_ptr
初始化。
std::shared_ptr<int> sp = std::make_shared<int>(10);
std::weak_ptr<int> wp(sp);
2、访问对象:由于 weak_ptr
不拥有对象的所有权,因此不能直接通过 *
或 ->
操作符访问对象。需要先通过 lock()
方法将其转换为 shared_ptr
,然后才能访问对象。
if (std::shared_ptr<int> sp2 = wp.lock()) {
std::cout << *sp2 << std::endl;
} else {
std::cout << "wp指向的对象已被释放" << std::endl;
}
3、检查对象状态:
use_count()
:返回与weak_ptr
指向的shared_ptr
关联的引用计数(但请注意,这个值可能会立即变化,因为其他线程可能正在操作shared_ptr
)。expired()
:检查weak_ptr
指向的shared_ptr
是否已被销毁。如果返回true
,则表示对象已被释放,此时lock()
将返回一个空的shared_ptr
。
注意事项:
1、weak_ptr
不应单独使用,它总是与 shared_ptr
配合使用。
2、由于 weak_ptr
不增加引用计数,因此它不能单独保证对象的生命周期。当所有 shared_ptr
被销毁时,即使还有 weak_ptr
指向该对象,对象也会被释放。
3、在多线程环境中使用 weak_ptr
时,需要特别注意线程安全问题。由于 use_count()
返回的值可能会立即变化,因此它不能用于多线程中的同步或协调。
觉得有帮助的话,打赏一下呗。。