unique_ptr 和 shared_ptr

发布时间 2023-12-06 17:20:25作者: ponder776

`unique_ptr` 和 `shared_ptr` 是 C++ 标准库中的智能指针,用于管理动态分配的对象的生命周期,以避免内存泄漏和手动资源管理的问题。

1. **`unique_ptr`:**
- `std::unique_ptr` 是一个独占所有权的智能指针,确保在任何时候只有一个 `unique_ptr` 拥有对动态分配的对象的所有权。
- 当 `unique_ptr` 被销毁或通过 `std::move` 转移所有权时,关联的对象会被正确释放。
- `unique_ptr` 的性能开销较小,因为它不需要维护引用计数。

```cpp
#include <memory>

std::unique_ptr<int> uniquePtr = std::make_unique<int>(42);
```

2. **`shared_ptr`:**
- `std::shared_ptr` 允许多个智能指针共享对同一对象的所有权,通过引用计数来跟踪对象的共享情况。
- 当最后一个拥有 `shared_ptr` 的实例被销毁时,关联的对象会被释放。
- `shared_ptr` 的使用相对较方便,但由于引用计数的管理,可能涉及一些性能开销。

```cpp
#include <memory>

std::shared_ptr<int> sharedPtr1 = std::make_shared<int>(42);
std::shared_ptr<int> sharedPtr2 = sharedPtr1; // 共享所有权
```

选择 `unique_ptr` 还是 `shared_ptr` 取决于你的需求。如果能确保对象只有一个所有者,使用 `unique_ptr` 可以更轻量和高效。如果需要多个地方共享对象所有权,使用 `shared_ptr`。注意,`shared_ptr` 的引用计数机制可能导致循环引用问题,可以通过 `std::weak_ptr` 来解决。

`std::weak_ptr` 是 C++ 中的智能指针,用于解决循环引用的问题。它不会增加引用计数,因此不会导致循环引用问题。在使用 `std::weak_ptr` 时,需要配合 `std::shared_ptr` 使用。以下是一个简单的示例:

```cpp
#include <iostream>
#include <memory>

class B; // 前置声明

class A {
public:
std::shared_ptr<B> b_ptr;

A() {
std::cout << "A constructor" << std::endl;
}

~A() {
std::cout << "A destructor" << std::endl;
}
};

class B {
public:
std::weak_ptr<A> a_weak_ptr;

B() {
std::cout << "B constructor" << std::endl;
}

~B() {
std::cout << "B destructor" << std::endl;
}
};

int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();

a->b_ptr = b;
b->a_weak_ptr = a; // 使用 std::weak_ptr

// 此时 a 和 b 不再形成循环引用

return 0; // 程序结束,对象 A 和 B 的析构函数会被调用
}
```

在这个例子中,我们将 `a_ptr` 和 `b_ptr` 的类型改为 `std::weak_ptr`。这样,`std::weak_ptr` 不会增加对象引用计数,从而防止了循环引用。在 `main` 函数结束时,`std::shared_ptr` 的引用计数会减少,当减到零时,对象 A 和 B 的析构函数会被调用,资源得到释放。

需要注意的是,在使用 `std::weak_ptr` 时,需要通过 `lock` 函数将其转换为 `std::shared_ptr` 来访问对象。这是因为 `std::weak_ptr` 本身并不拥有对象,而是只是观察 `std::shared_ptr` 的状态。

`std::weak_ptr` 通过 `lock` 函数可以尝试将其转换为 `std::shared_ptr`,以便安全地访问被观察对象。如果 `std::shared_ptr` 对象已经被销毁,`lock` 将返回一个空的 `std::shared_ptr`。以下是一个使用 `lock` 函数的实例:

```cpp
#include <iostream>
#include <memory>

class B; // 前置声明

class A {
public:
std::shared_ptr<B> b_ptr;

A() {
std::cout << "A constructor" << std::endl;
}

~A() {
std::cout << "A destructor" << std::endl;
}
};

class B {
public:
std::weak_ptr<A> a_weak_ptr;

B() {
std::cout << "B constructor" << std::endl;
}

~B() {
std::cout << "B destructor" << std::endl;
}
};

int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();

a->b_ptr = b;
b->a_weak_ptr = a;

// 使用 lock 尝试将 weak_ptr 转换为 shared_ptr
std::shared_ptr<A> sharedA = b->a_weak_ptr.lock();

if (sharedA) {
// 成功转换,对象 A 仍然存在
std::cout << "Accessing A through shared_ptr" << std::endl;
} else {
// 转换失败,对象 A 已被销毁
std::cout << "Object A is no longer available" << std::endl;
}

return 0; // 程序结束,对象 A 和 B 的析构函数会被调用
}
```

在这个例子中,我们使用 `lock` 函数尝试将 `std::weak_ptr` `b->a_weak_ptr` 转换为 `std::shared_ptr<A>`。如果成功转换,就可以通过返回的 `std::shared_ptr` 安全地访问对象 A。如果对象 A 已经被销毁,`lock` 返回一个空的 `std::shared_ptr`。这样,我们可以在使用前检查 `shared_ptr` 是否为空,以避免访问已释放的对象。