首页 > 其他分享 >智能指针和互斥锁

智能指针和互斥锁

时间:2024-01-13 23:31:46浏览次数:35  
标签:std 智能 a1 互斥 shared unique ptr 指针

RAII : Resource Acquisition Is Initialization(资源获取即初始化)

class DynamicArray {
private:
    int* data; // 指向动态分配的数组的指针
public:
    DynamicArray(int size) {
        data = new int[size]; // 在构造函数中分配内存
    }
    
    ~DynamicArray() {
        delete[] data; // 在析构函数中释放内存
    }
};

void someFunction() {
    DynamicArray arr(10); // 在栈上创建DynamicArray对象,分配了一个长度为10的数组
    
    // 在这里,arr可以被使用,不需要手动释放内存
} // 在这里,arr离开了作用域,析构函数会被调用,释放内存

可以看到,上述函数中的局部对象是有作用域的,也就是在 { } 内,但当其结束生命周期的时候,堆区的内存并不会释放,如果不人工进行释放,则会造成资源泄露。 而如果程序很复杂的时候,需要为所有的new 分配的内存delete掉,导致效率下降,更可怕的是,程序的可理解性和可维护性明显降低了,当操作增多时,处理资源释放的代码就会越来越多,越来越乱。

在这种情况下引入了RAII思想来解决这类问题,RAII的做法是使用一个对象(因为对象的生命周期结束时一定会执行析构函数,所以可以在这上面做文章,把资源与类对象的生命周期绑定),在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,最后在对象析构的时候,释放构造时获取的资源。也就是在调用析构函数的时候,会自动释放其获取的资源。让局部变量和局部变量指向的内存空间的生命周期保持一致。

智能指针

在资源管理方面,智能指针(std::shared_ptr和std::unique_ptr)是RAII最具代表性的实现,使用了智能指针,可以实现自动的内存管理,再也不用担心忘记delete造成内存泄漏了。

C++ 标准模板库 STL(Standard Template Library)共提供了四种智能指针:

auto_ptr(已弃用,被unique_ptr代替)
unique_ptr
shared_ptr
weak_ptr

unique_ptr 拥有对持有对象的唯一所有权,不能被复制到另外一个unique_ptr ,所持有的对象只能通过转移语义将所有权转移到另外一个unique_ptr

std::unique_ptr<A> a1(new A());
std::unique_ptr<A> a2 = a1;//编译报错,不允许复制
std::unique_ptr<A> a3 = std::move(a1);//可以转移所有权,所有权转义后a1不再拥有任何指针

unique_ptr本身拥有的方法主要包括:

1、get() 获取其保存的原生指针,尽量不要使用

2、bool() 判断是否拥有指针

3、release() 释放所管理指针的所有权,返回原生指针。但并不销毁原生指针。

4、reset() 释放并销毁原生指针。如果参数为一个新指针,将管理这个新指针

std::unique_ptr<A> a1(new A());
A *origin_a = a1.get();//尽量不要暴露原生指针
if(a1)
{
    // a1 拥有指针
}
std::unique_ptr<A> a2(a1.release());//常见用法,转义拥有权
a2.reset(new A());//释放并销毁原有对象,持有一个新对象
a2.reset();//释放并销毁原有对象,等同于下面的写法
a2 = nullptr;//释放并销毁原有对象

shared_ptr强调的是共享所有权。也就是说多个shared_ptr可以拥有同一个原生指针的所有权。

shared_ptr 是通过引用计数的方式管理指针,当引用计数为 0 时会销毁拥有的原生对象。

shared_ptr本身拥有的方法主要包括:

1、get() 获取其保存的原生指针,尽量不要使用

2、bool() 判断是否拥有指针

3、reset() 释放并销毁原生指针。如果参数为一个新指针,将管理这个新指针

4、unique() 如果引用计数为 1,则返回 true,否则返回 false

5、use_count() 返回引用计数的大小

std::shared_ptr<A> a1(new A());
std::shared_ptr<A> a2 = a1;//编译正常,允许所有权的共享

A *origin_a = a1.get();//尽量不要暴露原生指针

if(a1)
{
    // a1 拥有指针
}

if(a1.unique())
{
    // 如果返回true,引用计数为1
}

long a1_use_count = a1.use_count();//引用计数数量 weak_ptr配合shared_ptr,由于shared_ptr是通过引用计数来管理原生指针的,那么最大的问题就是循环引用(比如 a 对象持有 b 对象,b 对象持有 a 对象),这样必然会导致内存泄露。而weak_ptr不会增加引用计数,因此将循环引用的一方修改为弱引用,可以避免内存泄露。

weak_ptr本身拥有的方法主要包括:

1、expired() 判断所指向的原生指针是否被释放,如果被释放了返回 true,否则返回 false

2、use_count() 返回原生指针的引用计数

3、lock() 返回 shared_ptr,如果原生指针没有被释放,则返回一个非空的 shared_ptr,否则返回一个空的 shared_ptr

4、reset() 将本身置空

std::shared_ptr<A> a1(new A());
std::weak_ptr<A> weak_a1 = a1;//不增加引用计数

if(weak_a1.expired())
{
    //如果为true,weak_a1对应的原生指针已经被释放了
}

long a1_use_count = weak_a1.use_count();//引用计数数量

if(std::shared_ptr<A> shared_a = weak_a1.lock())
{
    //此时可以通过shared_a进行原生指针的方法调用
}

weak_a1.reset();//将weak_a1置空

使用场景

这个对象在对象或方法内部使用时优先使用unique_ptr。

这个对象需要被多个 Class 同时使用的时候使用shared_ptr

shared_ptr 的实践

class B
{
private:
    std::shared_ptr<A> a_;

public:
    B(std::shared_ptr<A>& a): a_(a) {}
};

class C
{
private:
    std::shared_ptr<A> a_;

public:
    C(std::shared_ptr<A>& a): a_(a) {}
};

std::shared_ptr<B> b_;
std::shared_ptr<C> c_;

void test_A_B_C()
{
    std::shared_ptr<A> a = std::make_shared<A>();//使用std::make_shared创建shared_ptr。

    b_ = std::make_shared<B>(a);
    c_ = std::make_shared<C>(a);
}

在上面的代码中需要注意,我们使用std::make_shared代替new的方式创建shared_ptr。

因为使用new的方式创建shared_ptr会导致出现两次内存申请,而std::make_shared在内部实现时只会申请一个内存。因此建议后续均使用std::make_shared。

如果A想要调用B和C的方法怎么办呢?可否在A中定义B和C的shared_ptr呢?答案是不可以,这样会产生循环引用,导致内存泄露。

此时就需要weak_ptr出场了。

class A
{
private:
    std::weak_ptr<B> b_;
    std::weak_ptr<C> c_;
public:
    void do_something() {}

    void set_B_C(const std::shared_ptr<B>& b, const std::shared_ptr<C>& c)
    {
        b_ = b;
        c_ = c;
    }
};

a->set_B_C(b_, c_);

如果想要在A内部将当前对象的指针共享给其他对象,需要怎么处理呢?

class D
{
private:
    std::shared_ptr<A> a_;

public:
    std::shared_ptr<A>& a): a_(a) {}
};

class A
{
//上述代码省略

public:
    void new_D()
    {
        //错误方式,用this指针重新构造shared_ptr,将导致二次释放当前对象
        std::shared_ptr<A> this_shared_ptr1(this);
        std::unique_ptr<D> d1(new D(this_shared_ptr1));
    }
};

如果采用this指针重新构造shared_ptr是肯定不行的,因为重新创建的shared_ptr与当前对象的shared_ptr没有关系,没有增加当前对象的引用计数。这将导致任何一个shared_ptr计数为 0 时提前释放了对象,后续操作这个释放的对象都会导致程序异常。

此时就需要引入shared_from_this。对象继承了enable_shared_from_this后,可以通过shared_from_this()获取当前对象的shared_ptr指针。

class A: public std::enable_shared_from_this<A>
{
//上述代码省略

public:
    void new_D()
    {
        //错误方式,用this指针重新构造shared_ptr,将导致二次释放当前对象
        std::shared_ptr<A> this_shared_ptr1(this);
        std::unique_ptr<D> d1(new D(this_shared_ptr1));
        //正确方式
        std::shared_ptr<A> this_shared_ptr2 = shared_from_this();
        std::unique_ptr<D> d2(new D(this_shared_ptr2));
    }
};

注意:尽量不要混用智能指针和原生指针等一系列可能导致对象二次销毁的操作!

在状态管理方面,线程同步中使用std::unique_lock或std::lock_guard对互斥量std::mutex进行状态管理也是RAII的典型实现,通过这种方式,我们再也不用担心互斥量之间的代码出现异常而造成线程死锁。

标签:std,智能,a1,互斥,shared,unique,ptr,指针
From: https://blog.51cto.com/u_14882565/9234215

相关文章

  • 字符指针与字符数组的初始化
    字符指针可以初始化赋值一个字符串,字符数组初始化也可以赋值一个字符串。两者的区别是什么呢?#include<stdio.h>#include<string.h>intmain(){char*p="hello";//把字符串常量"hello"的首地址赋给pcharc[10]="hello";//等价于strcpy(c,"hello");c[......
  • C++ --- 智能指针
    一、智能指针存在的意义智能指针主要解决以下问题:(1)内存泄漏:内存手动释放,使用智能指针可以自动释放。(2)共享所有权指针的传播和释放,比如多线程使用同一个对象时析构问题。 智能指针的实现依赖于C++语言的RAII(资源获取即初始化)技术,即资源的获取和释放应该与对象的构造和析构分......
  • 智能电网中的安全数据聚合方案
    基于秘密分享实现参考:基于秘密共享和同态加密的隐私数据融合方案-陈信系统模型三层架构:电力供应商(PS)基站(BS)智能电表(SM)第三方聚合器(TPA)可信第三方机构(TA):生成和分发随机数控制中心(CC)敌手模型可信:可信第三方机构(TA)、控制中心(CC)、电力供应商(PS)半诚实:基站(BS)、智能电......
  • 2023-2024年人工智能最佳工具(3)
    随着人工智能领域的迅速进展,我们目睹了越来越多令人惊叹和新奇的应用程序的诞生。这些应用程序在商业和个人领域都具有广泛的应用范围。我们使用这些人工智能工具的主要原因可以概括为一个共同点:人工智能已经发展到能够帮助人类完成繁重的、重复的任务,并减少人为错误,从而节约运营......
  • Springcloud智慧工地管理云平台源码 AI智能识别
    智慧工地解决方案一、现状描述 建筑工程建设具有明显的生产规模大宗性与生产场所固定性的特点。建筑企业70%左右的工作都发生在施工现场,施工阶段的现场管理对工程成本、进度、质量及安全等至关重要。同时随着工程建设规模不断扩大,工艺流程纷繁复杂,如何搞好现场施工现场管理,控制......
  • 书籍推荐-《人工智能数学基础》
    书籍:EssentialMathforAI:Next-LevelMathematicsforEfficientandSuccessfulAISystems作者:HalaNelson出版:O'ReillyMedia本文内容转载自公众号 【一点人工一点智能】关注51CTO @一点人工一点智能,了解更多移动机器人&人工智能信息01  书籍介绍许多行业都渴望将人工......
  • Microsoft 365 新功能速递:Microsoft EdgeManagement中的企业安全人工智能控件
    51CTO博客链接:https://blog.51cto.com/u_13637423Microsoft在1月9日发布了MicrosoftEdgeManagementService预览版,是在Microsoft365管理中心为MicrosoftEdge提供的一种新的、专用的、简化的管理体验。MicrosoftEdgeManagementService中的企业安全人工智能控制现在正在推出,以供......
  • 抖音利用人工智能操控用户行为?商家、达人该乐了
    文|新熔财经作者|一城字节跳动早期投资人陈伟星在社交平台上狂喷抖音,引发不小的震动。“运用人工智能对用户进行操控,引导其行为,最终实现抖音自身的意图目标”、“控制人类意识,沉迷于意识形态的皇帝之位”,这样的指控不可谓不尖锐。事实上,早在漂亮国大选期间,tiktok就被指控有类似的行动......
  • 基于AI视频智能分析技术的周界安全防范方案
    一、背景分析随着科技的不断进步,AI视频智能检测技术已经成为周界安全防范的一种重要手段。A智能分析网关V4基于深度学习和计算机视觉技术,可以通过多种AI周界防范算法,实时、精准地监测人员入侵行为,及时发现异常情况并发出警报,保障周界安全。智能分析网关V4内置了近40种AI算法模型,支......
  • 如何使用人工智能优化 DevOps?
    DevOps和人工智能密不可分,影响着各种业务。DevOps可以加快产品开发速度并简化现有部署的维护,而AI则可以改变整个系统的功能。DevOps团队可以依靠人工智能和机器学习来进行数据集成、测试、评估和发布系统。更重要的是,人工智能和机器学习可以以高效、快速、安全的方式改进DevOp......