当前位置: 首页> 汽车> 时评 > 网页设计免费模板网站推荐_上海新增感染呈下降趋势_淘宝推广哪种方式最好_长春百度网站优化

网页设计免费模板网站推荐_上海新增感染呈下降趋势_淘宝推广哪种方式最好_长春百度网站优化

时间:2025/7/10 2:24:27来源:https://blog.csdn.net/m0_64014551/article/details/146301060 浏览次数: 1次
网页设计免费模板网站推荐_上海新增感染呈下降趋势_淘宝推广哪种方式最好_长春百度网站优化

【c++】【智能指针】shared_ptr底层实现

智能指针之前已经写过了,但是考虑到不够深入,应该再分篇写写。

1 shared_ptr

1.1 shared_ptr 是什么

std::shared_ptr是一个类模板,它的对象行为像指针,但是它还能记录有多少个对象共享它管理的内存对象
多个std::shared_ptr可以共享同一个对象
最后一个std::shared_ptr被销毁时,它会自动释放它所指向的对象


1.2 shared_ptr创建和销毁

可以通过make_shared<>函数来创建。
可以通过拷贝或赋值另一个shared_ptr来创建
eg:sp1和sp2指向同一个对象,内存对象的引用计数为2。当sp1被销毁时,引用计数减为1,sp2仍然指向该对象。当sp2被销毁时,引用计数减为0,内存对象被销毁。
在这里插入图片描述


1.3 shared_ptr的底层原理

element_type*    _M_ptr;         // 所管理对象的地址.
__shared_count<_Lp>  _M_refcount;    // 引用计数块地址.

在这里插入图片描述
std::shared_ptr在内部其只有两个指针成员:

  • 一个指针是所管理的数据的地址
  • 一个指针是控制块的地址
    包括引用计数
    weak_ptr计数
    删除器(Deleter)
    分配器(Allocator)

因为不同shared_ptr指针需要共享相同的内存对象,因此引用计数的存储是在 上的。
而unique_ptr只有一个指针成员,指向所管理的数据的地址。因此一个shared_ptr对象的大小是它大小的倍。
eg:vs2022-64下:

int main()
{std::cout << sizeof(std::shared_ptr<int>) << std::endl; // 8std::cout << sizeof(std::unique_ptr<int>) << std::endl; // 4
}

在这里插入图片描述


1.4 std::shared_ptr的简单实现

我们通过下面这个简单的类来模拟std::shared_ptr<>的实现,来理解引用计数的实现原理。
这里我们为了简单,只实现了

  • shared_ptr的拷贝构造函数析构函数赋值运算符函数引用计数只是简单地用了一个int类型的内存空间
  • 省略了weak_ptr的计数、删除器和分配器,不考虑多线程的情况。
  • 当我们销毁一个shared_ptr时,引用计数减1。当引用计数减为0时,我们删除指向实际数据的指针和指向引用计数的指针。
  • 当我们拷贝一个shared_ptr时,引用计数加1。
  • 当我们赋值一个shared_ptr时,我们首先递减左侧运算对象的引用计数。如果引用计数变为0,我们就释放左侧运算对象分配的内存以及引用计数的内存。然后拷贝右侧运算对象的数据指针和引用计数指针,最后递增引用计数。
template<typename T>
class shared_ptr {
public:// 构造函数  // 初始化智能指针,传入一个裸指针(默认为 nullptr)  shared_ptr(T* ptr = nullptr) : m_ptr(ptr), m_refCount(new int(1)) {}// 拷贝构造函数  // 通过另一个 shared_ptr 构造,增加引用计数  shared_ptr(const shared_ptr& other) : m_ptr(other.m_ptr), m_refCount(other.m_refCount) {// 增加引用计数  (*m_refCount)++;}// 析构函数  ~shared_ptr() {// 减少引用计数  (*m_refCount)--;// 如果引用计数为 0,释放内存  if (*m_refCount == 0) {delete m_ptr;delete m_refCount;}}// 重载赋值运算符  shared_ptr& operator=(const shared_ptr& other) {// 检查自我赋值  if (this != &other) {// 减少旧对象的引用计数  (*m_refCount)--;// 如果引用计数为 0,释放内存  if (*m_refCount == 0) {delete m_ptr;delete m_refCount;}// 复制新对象的数据和引用计数指针,并增加引用计数  m_ptr = other.m_ptr;m_refCount = other.m_refCount;// 增加引用计数  (*m_refCount)++;}return *this;}private:T* m_ptr;            // 指向实际数据的指针  int* m_refCount;     // 引用计数  
};

ps:多线程时 引用计数的++操作需要是原子性的。考虑使用std::atomic

  • 是 C++11 引入的模板类,位于头文件 中,主要用于在多线程环境下实现原子操作,从而避免数据竞争(data race),保证线程安全。

1.5 什么时候用 std::shared_ptr<T>

std::shared_ptr<T> 主要用于以下场景:
1. 资源创建昂贵、比较耗时的场景

  • 创建对象代价很高(例如文件、网络、数据库等),不希望频繁创建和销毁。
  • 通过共享指针来管理对象生命周期,避免频繁创建和释放导致的性能损耗。

示例:管理数据库连接

#include <iostream>
#include <memory>class Database {
public:Database() { std::cout << "Connecting to Database\n"; }~Database() { std::cout << "Closing Database Connection\n"; }
};void useDatabase(std::shared_ptr<Database> db) {std::cout << "Using Database\n";
}int main() {auto db = std::make_shared<Database>(); // 资源创建昂贵,使用共享指针管理useDatabase(db); // 共享所有权
}

在这个例子中,Database连接创建和释放都很昂贵,使用 shared_ptr 可以在多个对象间安全地共享连接(并非多线程),等所有使用者都释放之后才会关闭连接。

  • std::shared_ptr` 的引用计数是线程安全的(即对引用计数的增加和减少是原子操作,不会导致竞争条件)
  • 对实际管理的对象的操作是非线程安全的
    ps: 之前的文章有提到

在示例中,以下部分是线程安全的:

  • 引用计数的增加和减少
  • 判断对象是否需要释放
  • 只读访问是线程安全的

需要加锁的部分:

  • 对实际对象(Database)的数据修改需要加锁。
    如果多个线程同时修改 Database 对象的内容,可能会发生数据竞争,导致未定义行为。因此,需要在访问或修改对象时加锁

2. 需要共享资源的所有权

  • 一个对象的生命周期可能同时涉及多个对象,但不清楚谁会最终释放这个对象。
  • std::shared_ptr 使用引用计数,确保在最后一个 shared_ptr 离开作用域时才释放资源。

示例:对象被多个对象共享

#include <iostream>
#include <memory>class A {
public:A() { std::cout << "A constructed\n"; }~A() { std::cout << "A destroyed\n"; }
};int main() {std::shared_ptr<A> sp1 = std::make_shared<A>();std::shared_ptr<A> sp2 = sp1; // 引用计数 +1std::shared_ptr<A> sp3 = sp2; // 引用计数 +1std::cout << "Use count: " << sp1.use_count() << "\n"; // 输出 3sp2.reset(); // 引用计数 -1std::cout << "Use count after sp2 reset: " << sp1.use_count() << "\n"; // 输出 2sp1.reset(); // 引用计数 -1std::cout << "Use count after sp1 reset: " << sp3.use_count() << "\n"; // 输出 1sp3.reset(); // 最终释放对象
}
  • sp1, sp2, sp3 共享对 A 对象的所有权。
  • 只有最后一个 shared_ptr 释放时,才会析构 A 对象。

不适用 shared_ptr的情况
1. 存在明显的所有者 → 使用 unique_ptr 更合适。
2. 不需要共享所有权 → 使用裸指针或 unique_ptr
3. 循环引用问题 → 使用 weak_ptr 解决。

部分转自:https://zhuanlan.zhihu.com/p/672745555?utm_source=chatgpt.com

关键字:网页设计免费模板网站推荐_上海新增感染呈下降趋势_淘宝推广哪种方式最好_长春百度网站优化

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: