C++共享指针shared_ptr的理解分享

2024-06-12 18:44

本文主要是介绍C++共享指针shared_ptr的理解分享,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

share_ptr是线程安全的吗

回答:

  • 如果多个线程同时拷贝同一个shared_ptr对象,不会有问题,因为shared_ptr的引用技术是线程安全的。
  • 如果多个线程同时修改同一个shared_ptr对象,不是线程安全的。
  • 如果多个线程同时读写shared_ptr指向的内存对象,不是线程安全的。

关键点

  • 引用计数的安全性:std::shared_ptr的引用计数是原子的,这意味着增加或减少引用计数的操作是线程安全的,即使这些操作在多个线程中同时进行也不会出现问题。
  • 赋值操作的不安全性:std::shared_ptr的赋值操作(包括拷贝构造和赋值运算符)涉及多个步骤,这些操作在多个线程中同时进行时可能不安全,因为它们不是原子的。
  • 对象访问的不安全性:std::shared_ptr不会为被管理的对象提供任何线程安全的保障。如果多个线程尝试读取或修改被管理对象的状态,必须使用锁或其他同步机制来确保线程安全。
  • 构造和析构的不安全性:如果多个线程同时创建或销毁std::shared_ptr实例,虽然引用计数的增减是线程安全的,但与对象关联的构造和析构操作可能需要额外的同步来避免竞态条件。

std::shared_ptr主要由一下关键部分组成

  • 控制块:存储引用计数(use_count:跟踪有多少std::shared_ptr实例指向同个对象)和弱引用计数(weak_count:跟踪有多少std::weak_ptr实例指向同一个控制块)。
  • 智能指针对象:持有指向控制块的指针和指向托管对象的指针。

核心操作

  • 构造和析构。
  • 复制构造和赋值。
  • 释放对象(当引用计数降到0时)。
  • 增加和减少引用计数。
  • 获取和设置指向的原始指针。

使用样例

#include <iostream>
#include <memory>struct BigObj {BigObj() {std::cout << "big object has been constructed" << std::endl;}~BigObj() {std::cout << "big object has been destructed" << std::endl;}
};void test_ref() {std::shared_ptr<BigObj> sp1 = std::make_shared<BigObj>();   // 调用了BigObj构造std::cout << sp1.use_count() << std::endl;  // 1std::shared_ptr<BigObj> sp2 = sp1;   std::cout << sp2.use_count() << std::endl;  // 2std::shared_ptr<BigObj> sp3 = sp2; std::cout << sp3.use_count() << std::endl;  // 3std::cout << sp1.use_count() << std::endl;  // 3    
}void test_ref1() {std::shared_ptr<BigObj> sp1 = std::make_shared<BigObj>();   // 调用了BigObj构造std::cout << sp1.use_count() << std::endl;  // 1{std::shared_ptr<BigObj> sp2 = sp1;  std::cout << sp1.use_count() << std::endl; // 2   }std::cout << sp1.use_count() << std::endl;  // 1BigObj* ptr = sp1.get(); sp1 = nullptr;std::cout << sp1.use_count() << std::endl;  // 0
}int main() {test_ref();   // 程序生命周期结束,即使引用计数不是0,智能指针也进行资源析构test_ref1();  // 当引用计数变为0,它会调用控制块中存储的析构函数来销毁资源。}

样例输出结果

big object has been constructed
1
2
3
3
big object has been destructed
big object has been constructed
1
2
1
big object has been destructed
0

shared_ptr的析构操作

  • 减少其内部控制块中的引用计数。
  • 如果引用计数变为0,它会调用控制块中存储的析构函数来销毁资源。
  • 最后,它会减少控制块的弱引用计数,并如果弱引用计数也变为0,那么整个控制块将被销毁。
    因此,当一个函数结束时,如果该函数内定义的std::shared_ptr是唯一引用某个对象的实例,那么该对象将在函数结束时被自动销毁。这是std::shared_ptr提供的一种自动资源管理机制,有助于防止内存泄漏和资源泄露。

shared_ptr简单代码示例

template<typename T>
class shared_ptr {
private:T* ptr; // 指向分配的内存资源size_t* ref_count; // 引用计数public:// 构造函数explicit shared_ptr(T* p = nullptr) : ptr(p), ref_count(new size_t(1)) {}// 拷贝构造函数shared_ptr(const shared_ptr<T>& other) : ptr(other.ptr), ref_count(other.ref_count) {(*ref_count)++;}// 移动构造函数shared_ptr(shared_ptr<T>&& other) noexcept : ptr(other.ptr), ref_count(other.ref_count) {other.ptr = nullptr;other.ref_count = nullptr;}// 析构函数~shared_ptr() {release();}// 重载赋值运算符shared_ptr<T>& operator=(const shared_ptr<T>& other) {if (this != &other) {release();ptr = other.ptr;ref_count = other.ref_count;(*ref_count)++;}return *this;}// 重载移动赋值运算符shared_ptr<T>& operator=(shared_ptr<T>&& other) noexcept {if (this != &other) {release();ptr = other.ptr;ref_count = other.ref_count;other.ptr = nullptr;other.ref_count = nullptr;}return *this;}// 获取指针指向的对象T* get() const { return ptr; }// 获取引用计数size_t use_count() const { return (ref_count != nullptr) ? *ref_count : 0; }// 重载箭头操作符T* operator->() const { return ptr; }// 解引用操作符T& operator*() const { return *ptr; }// 释放资源void release() {if (ref_count != nullptr) {(*ref_count)--;if (*ref_count == 0) {delete ptr;delete ref_count;}}}// 重置指针void reset(T* p = nullptr) {release();ptr = p;ref_count = new size_t(1);}
};

这篇关于C++共享指针shared_ptr的理解分享的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1055027

相关文章

C++ STL-string类底层实现过程

《C++STL-string类底层实现过程》本文实现了一个简易的string类,涵盖动态数组存储、深拷贝机制、迭代器支持、容量调整、字符串修改、运算符重载等功能,模拟标准string核心特性,重点强... 目录实现框架一、默认成员函数1.默认构造函数2.构造函数3.拷贝构造函数(重点)4.赋值运算符重载函数

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

c++日志库log4cplus快速入门小结

《c++日志库log4cplus快速入门小结》文章浏览阅读1.1w次,点赞9次,收藏44次。本文介绍Log4cplus,一种适用于C++的线程安全日志记录API,提供灵活的日志管理和配置控制。文章涵盖... 目录简介日志等级配置文件使用关于初始化使用示例总结参考资料简介log4j 用于Java,log4c

C++归并排序代码实现示例代码

《C++归并排序代码实现示例代码》归并排序将待排序数组分成两个子数组,分别对这两个子数组进行排序,然后将排序好的子数组合并,得到排序后的数组,:本文主要介绍C++归并排序代码实现的相关资料,需要的... 目录1 算法核心思想2 代码实现3 算法时间复杂度1 算法核心思想归并排序是一种高效的排序方式,需要用

Linux从文件中提取特定内容的实用技巧分享

《Linux从文件中提取特定内容的实用技巧分享》在日常数据处理和配置文件管理中,我们经常需要从大型文件中提取特定内容,本文介绍的提取特定行技术正是这些高级操作的基础,以提取含有1的简单需求为例,我们可... 目录引言1、方法一:使用 grep 命令1.1 grep 命令基础1.2 命令详解1.3 高级用法2

深入理解go中interface机制

《深入理解go中interface机制》本文主要介绍了深入理解go中interface机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前言interface使用类型判断总结前言go的interface是一组method的集合,不

redis中session会话共享的三种方案

《redis中session会话共享的三种方案》本文探讨了分布式系统中Session共享的三种解决方案,包括粘性会话、Session复制以及基于Redis的集中存储,具有一定的参考价值,感兴趣的可以了... 目录三种解决方案粘性会话(Sticky Sessions)Session复制Redis统一存储Spr

使用Redis快速实现共享Session登录的详细步骤

《使用Redis快速实现共享Session登录的详细步骤》在Web开发中,Session通常用于存储用户的会话信息,允许用户在多个页面之间保持登录状态,Redis是一个开源的高性能键值数据库,广泛用于... 目录前言实现原理:步骤:使用Redis实现共享Session登录1. 引入Redis依赖2. 配置R

使用IDEA部署Docker应用指南分享

《使用IDEA部署Docker应用指南分享》本文介绍了使用IDEA部署Docker应用的四步流程:创建Dockerfile、配置IDEADocker连接、设置运行调试环境、构建运行镜像,并强调需准备本... 目录一、创建 dockerfile 配置文件二、配置 IDEA 的 Docker 连接三、配置 Do

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3