主题
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++ 中的动态内存,避免内存泄漏和循环引用等常见问题。