教程 cpp tutorial

shared_ptr 和 weak_ptr

在 C++ 中,shared_ptr 和 weak_ptr 是智能指针的一种,旨在提供更安全、自动的内存管理方式。它们属于 C++11 标准库中的智能指针,通常用于管理共享所有权和避免内存泄漏。它们解决了裸指针的许多问题,尤其是在复杂的对象管理中。

shared_ptr

shared_ptr 是一种智能指针,它允许多个指针共同拥有一个资源。当最后一个指向资源的 shared_ptr 被销毁时,资源会自动释放。它提供了引用计数的机制,用于追踪有多少个 shared_ptr 指向相同的对象。

创建 shared_ptr

shared_ptr 可以通过 std::make_shared 或构造函数来创建。推荐使用 make_shared,因为它比直接使用构造函数更加高效。

示例:创建 shared_ptr

cpp
#include <iostream>
#include <memory>  // 引入 shared_ptr 所在的头文件

using namespace std;

class MyClass {
public:
    MyClass() { cout << "MyClass Constructor\n"; }
    ~MyClass() { cout << "MyClass Destructor\n"; }
    void show() { cout << "Hello, MyClass!\n"; }
};

int main() {
    // 使用 make_shared 创建 shared_ptr
    shared_ptr<MyClass> ptr1 = make_shared<MyClass>();
    ptr1->show();
    
    // 使用 shared_ptr 构造函数创建
    shared_ptr<MyClass> ptr2 = shared_ptr<MyClass>(new MyClass());
    ptr2->show();

    // shared_ptr 会在最后一个指针超出作用域时自动释放内存
    return 0;
}

`

输出:

代码
MyClass Constructor
Hello, MyClass!
MyClass Constructor
Hello, MyClass!
MyClass Destructor
MyClass Destructor

shared_ptr 的常见操作

  • use_count():获取当前有多少个 shared_ptr 指向相同的对象。
  • reset():重置 shared_ptr,释放当前对象,并使其为空指针。
  • get():获取裸指针,但不影响引用计数。

示例:use_count 和 reset

cpp
#include <iostream>
#include <memory>

using namespace std;

class MyClass {
public:
    MyClass() { cout << "MyClass Constructor\n"; }
    ~MyClass() { cout << "MyClass Destructor\n"; }
    void show() { cout << "Hello, MyClass!\n"; }
};

int main() {
    shared_ptr<MyClass> ptr1 = make_shared<MyClass>();
    shared_ptr<MyClass> ptr2 = ptr1;  // 共享所有权
    cout << "Reference count: " << ptr1.use_count() << endl;  // 输出 2

    ptr2.reset();  // 重置 ptr2
    cout << "Reference count after reset: " << ptr1.use_count() << endl;  // 输出 1

    return 0;
}

输出:

代码
MyClass Constructor
Reference count: 2
Reference count after reset: 1
MyClass Destructor

weak_ptr

weak_ptr 是一种特殊的智能指针,它与 shared_ptr 配合使用,用于解决循环引用的问题。weak_ptr 不增加对象的引用计数,它仅仅观察资源是否存在。当 shared_ptr 被销毁时,weak_ptr 不会阻止资源的销毁。

为什么使用 weak_ptr?

在某些情况下,多个 shared_ptr 可能会相互引用,导致它们永远无法释放资源,形成循环引用。weak_ptr 可以打破循环引用,允许对象的生命周期由 shared_ptr 管理,同时避免了强引用导致的资源泄漏。

示例:使用 weak_ptr 解决循环引用

cpp
#include <iostream>
#include <memory>

using namespace std;

class A;

class B {
public:
    shared_ptr<A> a_ptr;  // B 持有 A 的 shared_ptr
};

class A {
public:
    weak_ptr<B> b_ptr;  // A 使用 weak_ptr 持有 B 的指针,避免循环引用
    void show() { cout << "Hello from A!\n"; }
};

int main() {
    shared_ptr<A> a = make_shared<A>();
    shared_ptr<B> b = make_shared<B>();
    
    a->b_ptr = b;  // A 持有 B 的弱引用
    b->a_ptr = a;  // B 持有 A 的强引用

    cout << "End of program\n";
    return 0;
}

输出:

代码
End of program

在此例中,A 和 B 之间的相互引用通过 weak_ptr 打破了循环引用,避免了内存泄漏。

weak_ptr 的常见操作

  • lock():将 weak_ptr 转换为 shared_ptr。如果对象已经被销毁,lock() 会返回一个空的 shared_ptr。
  • expired():检查所指向的对象是否已经被销毁。

示例:使用 weak_ptr 和 lock()

cpp
#include <iostream>
#include <memory>

using namespace std;

class MyClass {
public:
    MyClass() { cout << "MyClass Constructor\n"; }
    ~MyClass() { cout << "MyClass Destructor\n"; }
    void show() { cout << "Hello, MyClass!\n"; }
};

int main() {
    weak_ptr<MyClass> weakPtr;
    {
        shared_ptr<MyClass> sharedPtr = make_shared<MyClass>();
        weakPtr = sharedPtr;  // weak_ptr 观察 shared_ptr
        sharedPtr->show();

        // 使用 weak_ptr 的 lock() 获取 shared_ptr
        shared_ptr<MyClass> lockedPtr = weakPtr.lock();
        if (lockedPtr) {
            lockedPtr->show();
        }
    }  // sharedPtr 超出作用域,内存释放

    // 再次尝试 lock(),此时资源已销毁
    shared_ptr<MyClass> lockedPtr2 = weakPtr.lock();
    if (!lockedPtr2) {
        cout << "Object has been destroyed\n";  // 输出 Object has been destroyed
    }

    return 0;
}

输出:

代码
MyClass Constructor
Hello, MyClass!
Hello, MyClass!
MyClass Destructor
Object has been destroyed

总结

  • shared_ptr:实现了资源的共享所有权,通过引用计数管理内存,确保当所有 shared_ptr 都不再使用时,资源会自动释放。
  • weak_ptr:不增加引用计数,用于解决循环引用问题,它观察 shared_ptr 管理的资源,但不会阻止资源的销毁。

通过合理使用 shared_ptr 和 weak_ptr,可以高效且安全地管理 C++ 中的动态内存,避免内存泄漏和循环引用等常见问题。