C++:智能指针std::unique_ptr和std::shared_ptr

一、std::unique_ptr

  1. 独占所有权:std::unique_ptr 拥有对其管理的对象的唯一所有权,意味着没有两个 std::unique_ptr 指针可以指向同一个对象。
  2. 自动释放内存:当 std::unique_ptr 离开作用域或者被显式删除时,它所管理的对象会被自动删除(通过调用 delete 操作符)。
  3. 不能复制,只能移动:std::unique_ptr 不能被复制,但它可以通过移动语义(C++11 新引入的特性)来转移所有权。这确保了对象的唯一所有权。
  4. 定制删除器:你可以为 std::unique_ptr 指定一个自定义删除器,在析构时使用这个删除器来释放资源。
  5. 更低的开销:相对于 std::shared_ptrstd::unique_ptr 通常有更低的内存和性能开销,因为它不需要维护引用计数。

使用场景:当你需要确保对象不会被多个指针共享,而且你想要自动管理资源时,std::unique_ptr 是一个很好的选择。

二、std::shared_ptr

  1. 共享所有权:std::shared_ptr 允许多个指针实例共享对同一个对象的所有权。每个 std::shared_ptr 的复制都会增加一个内部引用计数。
  2. 自动释放内存:只有当最后一个 std::shared_ptr 指向对象的时候,对象才会被自动删除(通过调用 delete 操作符)。
  3. 可复制和移动:std::shared_ptr 支持复制和移动操作。复制时会增加引用计数,移动不会。
  4. 线程安全:std::shared_ptr 在修改对象的引用计数时是线程安全的,但这不代表它所指向的对象的操作也是线程安全的。
  5. 有开销:由于需要管理引用计数,std::shared_ptr 相对于 std::unique_ptr 有更大的内存和性能开销

使用场景:当你想要在应用程序中的多个部分之间共享对同一个对象的访问,且无需担心对象何时应该释放时,std::shared_ptr 很有用

三、代码举例

std::unique_ptr:

假设你有一个 Widget 类,并且要确保一个 Widget 在任何时候只由一个拥有者控制。比如你希望在一个函数中创建一个 Widget 的实例,并通过某些条件传递给另一个函数。

#include <iostream>
#include <memory>

class Widget {
public:
    Widget() { std::cout << "Widget constructed\n"; }
    ~Widget() { std::cout << "Widget destroyed\n"; }
    void doSomething() { std::cout << "Doing something\n"; }
};

void processWidget(std::unique_ptr<Widget> widgetPtr) {
    widgetPtr->doSomething();
}

int main() {
    std::unique_ptr<Widget> myWidgetPtr = std::make_unique<Widget>();
    // myWidgetPtr->doSomething();

    // Transfer ownership to processWidget function
    processWidget(std::move(myWidgetPtr));

    if (!myWidgetPtr) {
        std::cout << "myWidgetPtr no longer owns the Widget\n";
    }

    // If we uncomment the next line, it would not compile, 
    // because you cannot copy a unique_ptr:
    // std::unique_ptr<Widget> anotherWidgetPtr = myWidgetPtr; // Compile error
    
    return 0;
}

在这个例子中,myWidgetPtr 唯一地拥有 Widget 对象。当我们把它传递给 processWidget 函数时,我们使用 std::move 函数来转移所有权。一旦转移完成,myWidgetPtr 将不再拥有 Widget 对象。

std::shared_ptr:

#include <iostream>
#include <memory>

class Widget {
public:
    Widget() { std::cout << "Widget constructed\n"; }
    ~Widget() { std::cout << "Widget destroyed\n"; }
    void doSomething() { std::cout << "Doing something\n"; }
};

void processWidget(std::shared_ptr<Widget> widgetPtr) {
    std::cout << "Shared widget, use count: " << widgetPtr.use_count() << '\n';
    widgetPtr->doSomething();
}

int main() {
    std::shared_ptr<Widget> myWidgetPtr = std::make_shared<Widget>();

    {
        std::shared_ptr<Widget> myOtherWidgetPtr = myWidgetPtr;
        processWidget(myOtherWidgetPtr);
        std::cout << "Shared widget, use count inside scope: " << myWidgetPtr.use_count() << '\n';
    } // myOtherWidgetPtr goes out of scope here, decrementing use count

    std::cout << "Shared widget, use count outside scope: " << myWidgetPtr.use_count() << '\n';
    myWidgetPtr->doSomething();

    return 0;
}

在这个示例中,myWidgetPtr 是一个 std::shared_ptr,它共享拥有 Widget 对象。在代码块中,创建了一个 myOtherWidgetPtr 的拷贝,它和 myWidgetPtr 共享对同一个 Widget 对象的拥有权,并且内部引用计数增加。当 myOtherWidgetPtr 离开作用域时,引用计数减少,但因为 myWidgetPtr 仍然存在,所以 Widget 对象不会被删除。只有当所有 std::shared_ptr 实例都超出作用域,最后一个 std::shared_ptr 被销毁时,Widget 对象才会被删除。

四、std::enable_shared_from_this

std::enable_shared_from_this 是一个模板辅助类,它允许一个对象(由其自身的 std::shared_ptr 所拥有)安全地生成额外的 std::shared_ptr 实例,这些实例与初始的 std::shared_ptr 共享同样的控制块。

当您想在类内部获得指向当前对象的 std::shared_ptr 指针时,这非常有用,尤其是在您想在外部以 std::shared_ptr 接口管理对象生命周期的情况下。

下面是一个使用 std::enable_shared_from_this 的类的示例:

#include <memory>

class ACDCChargeTask : public std::enable_shared_from_this<ACDCChargeTask> {
public:
    // 类成员函数和变量

    std::shared_ptr<ACDCChargeTask> getSharedThis() {
        return shared_from_this();   // 返回一个指向当前对象的 std::shared_ptr
    }

    void someOtherFunction() {
        // 您可能需要在这里使用 shared_from_this() 的返回值
        auto sharedPtrToThis = shared_from_this();
        // 使用 sharedPtrToThis 做一些工作
    }
    
    // 更多类成员和函数...
};

在这个示例中,ACDCChargeTask 类通过公开继承 std::enable_shared_from_this 获得了 shared_from_this 方法,该方法可以用于获取指向当前对象 this 的 std::shared_ptr

请注意,为了安全使用 shared_from_this,对象必须已经被 std::shared_ptr 管理。也就是说,在使用 shared_from_this 方法之前,对象应该已经以 std::shared_ptr 形式存在。否则,调用 shared_from_this 将导致 std::bad_weak_ptr 异常。下面是如何正确创建并使用这个类的实例:

std::shared_ptr<ACDCChargeTask> task = std::make_shared<ACDCChargeTask>();
auto anotherSharedPtr = task->getSharedThis();

在这里,task 是一个 std::shared_ptr<ACDCChargeTask>,它拥有 ACDCChargeTask 的一个实例。之后,通过 task 调用 getSharedThis 方法返回了一个新的 std::shared_ptr<ACDCChargeTask>,它与 task 共享拥有权。

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
C++智能指针 智能指针_unique_ptr智能指针详解 智能指针详解 作为智能指针的⼀种,unique_ptr 指针⾃然也具备"在适当时机⾃动释放堆内存空间"的能⼒。和 shared_ptr 指针最⼤的不同之处在 于,unique_ptr 指针指向的堆内存⽆法同其它 unique_ptr 共享,也就是说,每个 unique_ptr 指针都独⾃拥有对其所指堆内存空间的所有 权。 这也就意味着,每个 unique_ptr 指针指向的堆内存空间的引⽤计数,都只能为 1,⼀旦该 unique_ptr 指针放弃对所指堆内存空 间的所有权,则该空间会被⽴即释放回收。 unique_ptr 智能指针是以模板类的形式提供的,unique_ptr<T>(T 为指针所指数据的类型)定义在<memory>头⽂件,并位于 std 命名空间 中。因此,要想使⽤ unique_ptr 类型指针,程序中应⾸先包含如下 2 条语句: 1. #include <memory> 2. using namespace std; 第 2 句并不是必须的,可以不添加,则后续在使⽤ unique_ptr 指针时,必须标注std:: 。 unique_ptr智能指针的创建 智能指针的创建 考虑到不同实际场景的需要,unique_ptr<T> 模板类提供了多个实⽤的构造函数,这⾥给读者列举了⼏种常⽤的构造 unique_ptr 智能指针的⽅式。 1) 通过以下 2 种⽅式,可以创建出空的 unique_ptr 指针: 1. std::unique_ptr<int> p1(); 2. std::unique_ptr<int> p2(nullptr); 2) 创建 unique_ptr 指针的同时,也可以明确其指向。例如: 1. std::unique_ptr<int> p3(new int); 由此就创建出了⼀个 p3 智能指针,其指向的是可容纳 1 个整数的堆存储空间。 和可以⽤ make_shared<T>() 模板函数初始化 shared_ptr 指针不同,C++11 标准中并没有为 unique_ptr 类型指针添 加类似的模板函数。 3) 基于 unique_ptr 类型指针不共享各⾃拥有的堆内存,因此 C++11 标准中的 unique_ptr 模板类没有提供拷贝构造函数,只提供 了移动构造函数。例如: 1. std::unique_ptr<int> p4(new int); 2. std::unique_ptr<int> p5(p4);//错误,堆内存不共享 3. std::unique_ptr<int> p5(std::move(p4));//正确,调⽤移动构造函数 值得⼀提的是,对于调⽤移动构造函数的 p4 和 p5 来说,p5 将获取 p4 所指堆空间的所有权,⽽ p4 将变成空指针(nullptr)。 4) 默认情况下,unique_ptr 指针采⽤ std::default_delete<T> ⽅法释放堆内存。当然,我们也可以⾃定义符合实际场景的释放规 则。值得⼀提的是,和 shared_ptr 指针不同,为 unique_ptr ⾃定义释放规则,只能采⽤函数对象的⽅式。例如: 1. //⾃定义的释放规则 2. struct myDel 3. { 4. void operator()(int *p) { 5. delete p; 6. } 7. }; 8. std::unique_ptr<int, myDel> p6(new int); 9. //std::unique_ptr<int, myDel> p6(new int, myDel()); unique_ptr<T>模板类提供的成员⽅法 模板类提供的成员⽅法 为了⽅便⽤户使⽤ unique_ptr 智能指针unique_ptr<T> 模板类还提供有⼀些实⽤的成员⽅法,它们各⾃的功能如表 1 所⽰。 表 1 unique_ptr指针可调⽤的成员函数 成员函数名 成员函数名 功 功 能 能 operator*() 获取当前 unique_ptr 指针指向的数据。 operator->() 重载 -> 号,当智能指针指向的数据类型为⾃定义的结构体时,通过 -> 运算符可以获取其内部的指定成员。 operator =() 重载了 = 赋值号,从⽽可以将 nullptr 或者⼀个右值 unique_ptr 指针直接赋值给当前同类型的 unique_ptr 指针。 operator []() 重载了 [] 运算符,当 unique_ptr 指针指向⼀个数组时,可以直接通过 [] 获取指定下标位置处的数据。 get() 获取当前 unique_ptr 指针内部包含的普通指针。 get_deleter()
unique_ptrC++11引入的智能指针类,定义在<memory>头文件中。它可以通过指定对象类型作为模板参数来创建。例如,unique_ptr<int>是指向int类型对象的指针,unique_ptr<double>是指向double类型对象的指针。\[1\] unique_ptr具有独占对象所有权的特点,即同一时间只能有一个unique_ptr指向一个对象,不能进行复制操作,只能进行移动操作。这意味着两个unique_ptr不能指向同一个对象。\[2\] unique_ptr还可以使用自定义的删除器(deleter),通过lambda表达式或函数指针来指定删除对象的方式。例如,在创建unique_ptr时可以指定一个lambda表达式作为删除器,用于在unique_ptr销毁时释放对象。\[3\] 总结来说,unique_ptrC++中一种独占所有权的智能指针,它提供了自动释放对象的功能,并且可以通过指定对象类型和自定义删除器来灵活地管理资源。 #### 引用[.reference_title] - *1* [C++ 智能指针unique_ptr](https://blog.csdn.net/weixin_42322256/article/details/124058138)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [智能指针 unique_ptr 详解](https://blog.csdn.net/fuhanghang/article/details/113928128)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值