unique_ptr 和 shared_ptr
unique_ptr 和 shared_ptr 是 C++ 标准库中的智能指针,用于管理动态分配的对象的生命周期,以避免内存泄漏和手动资源管理的问题。
-
unique_ptr:std::unique_ptr是一个独占所有权的智能指针,确保在任何时候只有一个unique_ptr拥有对动态分配的对象的所有权。- 当
unique_ptr被销毁或通过std::move转移所有权时,关联的对象会被正确释放。 unique_ptr的性能开销较小,因为它不需要维护引用计数。
#include <memory> std::unique_ptr<int> uniquePtr = std::make_unique<int>(42); -
shared_ptr:std::shared_ptr允许多个智能指针共享对同一对象的所有权,通过引用计数来跟踪对象的共享情况。- 当最后一个拥有
shared_ptr的实例被销毁时,关联的对象会被释放。 shared_ptr的使用相对较方便,但由于引用计数的管理,可能涉及一些性能开销。
#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 来解决。
循环引用(Circular Reference)是指两个或多个对象彼此之间相互引用,形成一个环状结构。在 C++ 中,当使用智能指针时,特别是 std::shared_ptr,循环引用可能导致内存泄漏,因为循环引用会导致对象的引用计数永远不会减为零,从而对象的析构函数不会被调用,资源无法正确释放。
考虑以下示例,其中两个对象(A 和 B)相互引用:
#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::shared_ptr<A> a_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_ptr = a;
// 此时 a 和 b 形成循环引用
return 0; // 程序结束,但对象 A 和 B 的析构函数不会被调用,内存泄漏
}
在上述例子中,对象 A 拥有一个指向对象 B 的 shared_ptr,而对象 B 也拥有一个指向对象 A 的 shared_ptr,这样就形成了循环引用。当 main 函数结束时,智能指针的引用计数不会减为零,因此对象 A 和 B 的析构函数不会被调用,导致内存泄漏。
为了解决这个问题,可以使用 std::weak_ptr 来打破循环引用,因为std::weak_ptr 不会增加引用计数。在上面的例子中,可以将 b_ptr 和 a_ptr 改为 std::weak_ptr 类型。这样,循环引用将不再阻止对象的析构函数被调用。
std::weak_ptr 是 C++ 中的智能指针,用于解决循环引用的问题。它不会增加引用计数,因此不会导致循环引用问题。在使用 std::weak_ptr 时,需要配合 std::shared_ptr 使用。以下是一个简单的示例:
#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 函数的实例:
#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 是否为空,以避免访问已释放的对象。
浙公网安备 33010602011771号