您当前的位置: 首页 >  c++

鱼儿-1226

暂无认证

  • 0浏览

    0关注

    1100博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

C++ – 智能指针的正确使用方式

鱼儿-1226 发布时间:2022-10-13 15:47:55 ,浏览量:0

C++11 中推出了三种智能指针,unique_ptr、shared_ptr 和 weak_ptr,同时也将 auto_ptr 置为废弃 (deprecated)。

但是在实际的使用过程中,很多人都会有这样的问题:

  1. 不知道三种智能指针的具体使用场景

  2. 无脑只使用 shared_ptr

  3. 认为应该禁用 raw pointer(裸指针,即 Widget * 这种形式),全部使用智能指针

本文将从这几方面讲解智能指针:

  1. 智能指针的应用场景分析
  2. 智能指针的性能分析: 为什么 shared_ptr 性能比 unique_ptr 差
  3. 指针作为函数参数时应该传,传值、传引用,还是裸指针?
1 对象所有权

首先需要理清楚的概念就是对象所有权的概念。所有权在 rust 语言中非常严格,写 rust 的时候必须要清楚自己创建的每个对象的所有权。

但是 C++ 比较自由,似乎我们不需要明白对象的所有权,写的代码也能正常运行。但是明白了对象所有权,我们才可以正确管理好对象生命周期和内存问题。

C++ 引入了智能指针,也是为了更好的描述对象所有权,简化内存管理,从而大大减少我们 C++ 内存管理方面的犯错机会。

2 std::unique_ptr 专属所有权

我们大多数场景下用到的应该都是 unique_ptr。

unique_ptr 代表的是专属所有权,即由 unique_ptr 管理的内存,只能被一个对象持有。 所以,unique_ptr 不支持复制和赋值,如下:

auto w = std::make_unique();
auto w2 = w; // 编译错误

C++

Copy

如果想要把 w 复制给 w2, 是不可以的。因为复制从语义上来说,两个对象将共享同一块内存。

因此,unique_ptr 只支持移动, 即如下:

auto w = std::make_unique();
auto w2 = std::move(w); // w2 获得内存所有权,w 此时等于 nullptr

C++

Copy

unique_ptr 代表的是专属所有权,如果想要把一个 unique_ptr 的内存交给另外一个 unique_ptr 对象管理。只能使用 std::move 转移当前对象的所有权。转移之后,当前对象不再持有此内存,新的对象将获得专属所有权。

如上代码中,将 w 对象的所有权转移给 w2 后,w 此时等于 nullptr,而 w2 获得了专属所有权。

2.1 性能

因为 C++ 的 zero cost abstraction 的特点,unique_ptr 在默认情况下和裸指针的大小是一样的。 所以 内存上没有任何的额外消耗,性能是最优的

2.2 使用场景1:忘记delete

unique_ptr 一个最简单的使用场景是用于类属性。代码如下:

class Box{
public:
    Box() : w(new Widget())
    {}

    ~Box()
    {
        // 忘记 delete w
    }
private:
    Widget* w;
};

C++

Copy

如果因为一些原因,w 必须建立在堆上。如果用裸指针管理 w,那么需要在析构函数中 delete w; 这种写法虽然没什么问题,但是容易漏写 delete 语句,造成内存泄漏。

如果按照 unique_ptr 的写法,不用在析构函数手动 delete 属性,当对象析构时,属性 w 将会自动释放内存。

2.3 使用场景2:异常安全

假如我们在一段代码中,需要创建一个对象,处理一些事情后返回,返回之前将对象销毁,如下所示:

void process()
{
    Widget* w = new Widget();
    w->do_something(); // 可能会发生异常
    delete w;
}

C++

Copy

在正常流程下,我们会在函数末尾 delete 创建的对象 w,正常调用析构函数,释放内存。

但是如果 w->do_something() 发生了异常,那么 delete w 将不会被执行。此时就会发生 内存泄漏。 我们当然可以使用 try…catch 捕捉异常,在 catch 里面执行 delete,但是这样代码上并不美观,也容易漏写。

如果我们用 std::unique_ptr,那么这个问题就迎刃而解了。无论代码怎么抛异常,在 unique_ptr 离开函数作用域的时候,内存就将会自动释放。

2.4 使用场景3:STL容器

可将 unique_ptr 存储到 STL 容器中,只要对容器元素不使用拷贝操作的算法即可(如 sort())。例如,可在程序中使用类似于下面的代码段

unique_ptr make_int(int n) { return unique_ptr(new int(n)); }
void show(unique_ptr& p1) { cout             
关注
打赏
1604459285
查看更多评论
0.0455s