C++中的智能指针有哪些,各自有什么作用?
智能指针主要解决一个内存泄露的问题,它可以自动地释放内存空间。因为它本身是一个类,当函数结束的时候会调用析构函数,并由析构函数释放内存空间。智能指针分为共享指针(shared_ptr), 独占指针(unique_ptr)和弱指针(weak_ptr):
(1)shared_ptr ,多个共享指针可以指向相同的对象,采用了引用计数的机制,当最后一个引用销毁时,释放内存空间;
(2)unique_ptr,保证同一时间段内只有一个智能指针能指向该对象(可通过move操作来传递unique_ptr);
(3)weak_ptr,用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。
std::unique_ptr
定义
std::unique_ptr
是独占所有权的智能指针,一个对象只能被一个std::unique_ptr
管理,不能共享所有权。
主要用途
- 自动释放资源 :当
std::unique_ptr
超出作用域时,会自动释放所管理的资源。 - 独占所有权:确保同一资源不会被多个指针操作,避免潜在的内存问题。
- 高效传递所有权 :通过
std::move
可以将资源所有权转移。
示例代码
cpp
#include <iostream>
#include <memory>
void uniquePtrExample() {
std::unique_ptr<int> uptr1 = std::make_unique<int>(10); // 创建并管理资源
std::cout << "Value: " << *uptr1 << std::endl;
// std::unique_ptr<int> uptr2 = uptr1; // 错误!不能复制
std::unique_ptr<int> uptr2 = std::move(uptr1); // 转移所有权
if (!uptr1) {
std::cout << "uptr1 is now empty." << std::endl;
}
std::cout << "Value from uptr2: " << *uptr2 << std::endl;
}
int main() {
uniquePtrExample();
return 0;
}
输出
Value: 10
uptr1 is now empty.
Value from uptr2: 10
std::shared_ptr
定义
std::shared_ptr
是具有共享所有权的智能指针,多个std::shared_ptr
可以同时管理同一资源,资源会在最后一个std::shared_ptr
销毁时释放。
主要用途
- 共享资源的生命周期管理:适用于多个对象需要访问和管理同一资源的场景。
- 引用计数:内部维护一个引用计数,记录资源被引用的次数。
- 线程安全:引用计数是线程安全的。
示例代码
#include <iostream>
#include <memory>
void sharedPtrExample() {
std::shared_ptr<int> sptr1 = std::make_shared<int>(20); // 创建并管理资源
std::shared_ptr<int> sptr2 = sptr1; // 共享资源
std::cout << "Value: " << *sptr1 << ", Use count: " << sptr1.use_count() << std::endl;
sptr2.reset(); // sptr2释放共享的资源
std::cout << "After reset, Use count: " << sptr1.use_count() << std::endl;
}
int main() {
sharedPtrExample();
return 0;
}
输出
Value: 20, Use count: 2
After reset, Use count: 1
std::weak_ptr
定义
std::weak_ptr
是一种弱引用的智能指针,它不会增加资源的引用计数,通常与std::shared_ptr
配合使用,解决循环引用问题。
主要用途
- 解决循环引用问题 :避免
std::shared_ptr
之间的循环引用导致资源无法释放。 - 观察资源状态 :可以安全地观察
std::shared_ptr
管理的资源是否仍然存在。
示例代码
#include <iostream>
#include <memory>
void weakPtrExample() {
std::shared_ptr<int> sptr = std::make_shared<int>(30);
std::weak_ptr<int> wptr = sptr; // 创建弱引用
std::cout << "Use count: " << sptr.use_count() << std::endl;
if (auto sp = wptr.lock()) { // 检查资源是否有效
std::cout << "Value: " << *sp << std::endl;
}
sptr.reset(); // 释放资源
if (wptr.expired()) {
std::cout << "Resource has been released." << std::endl;
}
}
int main() {
weakPtrExample();
return 0;
}
输出
Use count: 1
Value: 30
Resource has been released.
区别分析
特性 | std::unique_ptr |
std::shared_ptr |
std::weak_ptr |
---|---|---|---|
所有权 | 独占所有权 | 共享所有权 | 无所有权,仅观察资源 |
引用计数 | 无 | 有 | 无 |
典型用途 | 独占资源管理 | 共享资源管理 | 解决循环引用问题 |
转移操作 | 通过std::move |
支持复制和共享 | 不直接管理资源 |
shared_ptr的实现原理是什么?构造函数、拷贝构造函数和赋值运算符怎么写?shared_ptr是不是线程安全的?
(1)shared_ptr是通过引用计数机制实现的,引用计数存储着有几个shared_ptr指向相同的对象,当引用计数下降至0时就会自动销毁这个对象;
(2)具体实现:
1)构造函数:将指针指向该对象,引用计数置为1;
2)拷贝构造函数:将指针指向该对象,引用计数++;
3)赋值运算符:=号左边的shared_ptr的引用计数-1,右边的shared_ptr的引用计数+1,如果左边的引用技术降为0,还要销毁shared_ptr指向对象,释放内存空间。
(3)shared_ptr的引用计数本身是安全且无锁的,但是它指向的对象的读写则不是,因此可以说shared_ptr不是线程安全的。[shared_ptr是线程安全的吗? - 云+社区 - 腾讯云 (tencent.com)](
shared_ptr 的循环引用
定义
shared_ptr
的循环引用(circular reference)是指两个或多个 shared_ptr
实例通过相互引用彼此共享的资源,导致引用计数无法归零,从而无法释放资源的问题。
问题原因
shared_ptr
通过引用计数来管理资源。- 如果两个
shared_ptr
互相引用,计数器永远不会归零,资源也就无法释放,造成 内存泄漏。
示例代码
以下是一个简单的循环引用示例:
c
#include <iostream>
#include <memory>
class Node {
public:
std::shared_ptr<Node> next;
~Node() { std::cout << "Node destroyed\n"; }
};
int main() {
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
// 创建循环引用
node1->next = node2;
node2->next = node1;
// 离开作用域,理论上 node1 和 node2 应该被销毁
return 0;
}
// 输出:
// (没有任何 "Node destroyed" 的输出)
在上述代码中:
node1
和node2
各自的shared_ptr
引用计数为 1。- 它们通过
next
成员指针互相引用,导致shared_ptr
的引用计数始终为 1。 - 离开作用域时,引用计数无法归零,析构函数不会被调用,资源泄漏。
解决方案:使用 weak_ptr
为了避免循环引用,可以使用 weak_ptr
替代其中一个 shared_ptr
,因为 weak_ptr
不会增加引用计数。
修正后的代码
c
#include <iostream>
#include <memory>
class Node {
public:
std::weak_ptr<Node> next; // 使用 weak_ptr 打破循环
~Node() { std::cout << "Node destroyed\n"; }
};
int main() {
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2;
node2->next = node1; // 使用 weak_ptr 不会增加引用计数
return 0;
}
// 输出:
// Node destroyed
// Node destroyed