智能指针不是实际的指针,而是一个对象,它通过重载 *、→ 方法实现类似指针的效果,并在通过析构函数,实现内存的自动回收。不仅如此,它还综合考虑了很多现实的应用场景,能够自动适应各种复杂的情况,防止误用指针导致的隐患,非常“聪明”,所以被称为“智能指针”。
unique_ptr<string> ptr2(newstring("hello")); // string智能指针
assert(*ptr2 == "hello"); // 可以使用*取内容
assert(ptr2->size() == 5); // 可以使用->调用成员函数
assert(ptr2 != nullptr); // 判断不为空指针
// ptr2++ // raise error: 没有定义加减运算,不能随意移动指针,避免越界
// delete ptr2; // raise error: ptr2 实际是一个对象,不能调用 delete
unique_ptr<int> ptr3; // 未初始化智能指针
// *ptr3 = 42 ; // 错误!操作了空指针
// 使用工厂函数可以确保在使用时初始化
auto ptr4 = make_unique<int>(42); // 工厂函数创建智能指针
unique_ptr 应用了 C++ 的“转移”(move)语义,同时禁止了拷贝赋值,所以,在向另一个 unique_ptr 赋值的时候,要特别留意,必须用 std::move() 函数显式地声明所有权转移。
赋值操作之后,指针的所有权就被转走了,原来的 unique_ptr 变成了空指针,新的unique_ptr 接替了管理权,保证所有权的唯一性。
shared_ptr 大多数情况下和 unique_ptr 一样,但最大的不同点在于:它的所有权是可以被安全共享的,也就是说支持拷贝赋值,允许被多个“人”同时持有,就像原始指针一样。
auto ptr1 = make_shared<int>(42); // 工厂函数创建智能指针
assert(ptr1 && ptr1.unique() ); // 此时智能指针有效且唯一
auto ptr2 = ptr1; // 直接拷贝赋值,不需要使用move()
assert(ptr1 && ptr2); // 此时两个智能指针均有效
assert(ptr1 == ptr2); // shared_ptr可以直接比较
// 两个智能指针均不唯一,且引用计数为2
assert(!ptr1.unique() && ptr1.use_count() == 2);
assert(!ptr2.unique() && ptr2.use_count() == 2);
通过引用计数,shared_ptr 可以实现安全贡献,在引用计数减少到 0 时,才会真正调用释放内存。
shared_ptr 可以在任何场合替代原始指针,但也存在引用计数的开销( 管理、存储 ),同时也会因为循环引用的问题而导致内存泄漏。
使用 weak_ptr 可以避免循环引用。它专门为打破循环引用而设计,只观察指针,不会增加引用计数(弱引用),但在需要的时候,可以调用成员函数 lock(),获取shared_ptr(强引用)。
class Node final {
public:
using this_type = Node;
// using shared_type = shared_ptr<this_type>; // 如果是这种写法会导致 next 循环引用
using shared_type = weak_ptr<this_type>;
shared_type next;
};
auto n1 = make_shared<Node>();
auto n2 = make_shared<Node>();
n1->next = n2;
n2->next = n1;
assert(n1.use_count() == 1); // 引用计数不会增加
assert(n2.use_count() == 1);
// 需要使用时
if (!n1->next.expired()) { // 检查指针是否有效
auto ptr = n1->next.lock(); // 真正获取 shared_ptr
assert(ptr == n2);
assert(n1.use_count() == 1);
assert(n2.use_count() == 2); // n2 的引用技术增加
}