首页 > 其他分享 >unique_ptr 和 shared_ptr

unique_ptr 和 shared_ptr

时间:2023-12-06 17:24:51浏览次数:25  
标签:std 对象 weak 引用 shared unique ptr

unique_ptrshared_ptr 是 C++ 标准库中的智能指针,用于管理动态分配的对象的生命周期,以避免内存泄漏和手动资源管理的问题。

  1. 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);
    
  2. 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_ptra_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_ptrb_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 是否为空,以避免访问已释放的对象。

标签:std,对象,weak,引用,shared,unique,ptr
From: https://www.cnblogs.com/whcjob/p/17879995.html

相关文章

  • nullptr关键字
    文章参考:爱编程的大丙(subingwen.cn)C使用NULL表示空,其定义为:#defineNULL((void*)0)在C++中同样定义了NULL,C和C++虽然都是强类型语言,但是C++对语言的要求更高,不允许(void*)类型进行隐式的类型转化,因此如果沿袭C中NULL的定义,那么下述代码会出错:int*p=NULL;因此C++......
  • OSCP(基础篇靶机Kioptrix Level 3)
    第一步:nmap与dirb第二步:发现 ProudlyPoweredby:LotusCMS框架,尝试是否存在漏洞利用(失败) 漏洞分析与利用:https://www.youtube.com/watch?v=ZsQP94HHfeY该模块利用了LotusCMS3.0的Router()函数中发现的漏洞。这是通过在“page”参数中嵌入PHP代码来完成的,该参数......
  • OSCP(基础篇靶机Kioptrix Level 2)
    第一步:netdiscover加nmap 第二步:通过burpsuite,利用SQLiPayloads进行SQL注入SQLiPayloads:'-''''&''^''*''or''-''or''''or''&''or'&#......
  • OSCP(基础篇靶机Kioptrix Level 1)
    第一步:netdiscover-ieth0 第二步:nmap 第三步:139samba高危服务,先看看是啥版本,利用msfmsfconsoleuseauxiliary/scanner/smb/smb_version 第四步:Samba<2.2.8(Linux/BSD)-RemoteCodeExecution存在远程代码执行漏洞kali查看某个系统的漏洞工具:msfconsole->sear......
  • linux启动mysql数据库,报错mysql: error while loading shared libraries: libtinfo.so
    如下 原因: 解决方案:1、在/usr/lib64目录里面找一个差不多名称版本的文件进行链接#软连接出一个新的文件sudoln-s/usr/lib64/libtinfo.so.6.1/usr/lib64/libtinfo.so.5若本服务器没有相近版本的文件2、从其他服务器下载一个libtinfo.so.5拷贝进去即可,或者下载相近版本......
  • C++标准库类std::shared_future
    std::shared_future是C++11的标准库类,其与std::future的不同是允许多个线程使用,多个线程可以同步共享,同时其又不会阻塞等待异步任务的完成。std::shared_future同样也提供get()方法用于获取异步执行的结果。#include<iostream>#include<thread>#include<future>void......
  • 2023-11-22 Invariant Violation: [app.model] namespace should be unique ==》模块
     如上图,报错原因:存在多个名为demoDataSource的模块名称导致报错解决方案:修改模块名称即可,把demoDataSource改为demoDataSource2就不会报错了扩展:该问题是由rudex引起的,redex要求数据模型(models)命名(namespace)必须不同,否则在注入该数据模型时就会报错......
  • 汇编-PTR指针
            ......
  • Mysql报:error while loading shared libraries libtinfo.so.5的解决办法
    版权声明:原创作品,谢绝转载!否则将追究法律责任。—————作者:kirin#、今天闲来无事,想在Anolis8的系统上装一个MySQL8.0玩。前期在安装和配置的过程中没有什么问题,但是在我想查看一下数据库版本的时候报了一个错。具体报错信息如下:mysql:errorwhileloadingsharedlibrarie......
  • 3分钟搞懂snmptrapd的配置与使用
    1.配置首先是snmptrap接收端的配置。默认应该没有这个文件的,你可以通过touch命令来创建snmptrapd.conf#创建trapd配置文件touchsnmptrapd.conf#打开snmptrapd.conf文件vimsnmptrapd.conf配置命令如下:#authcommunity是为了设置所有用户的访问权限:可执行,记录,传递。......