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
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值