应该使用unique_ptr来更轻松地实现“移动"语义? [英] Should use unique_ptr to more easily implement "move" semantics?

查看:92
本文介绍了应该使用unique_ptr来更轻松地实现“移动"语义?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:使FooBar变得不那么琐碎,并且直接替换为shared_ptr<>更加困难.

made Foo and Bar a little less trivial, and direct replacement with shared_ptr<> more difficult.

unique_ptr<>应该用作实现移动语义的更简单方法吗?

Should unique_ptr<> be used as an easier way to implement move semantics?

对于像这样的课程

class Foo
{
    int* m_pInts;
    bool usedNew;
    // other members ...

public:
    Foo(size_t num, bool useNew=true) : usedNew(useNew) {
        if (usedNew)
            m_pInts = new int[num];
        else
            m_pInts = static_cast<int*>(calloc(num, sizeof(int)));
    }
    ~Foo() {
        if (usedNew)
            delete[] m_pInts;
        else
            free(m_pInts);
    }

    // no copy, but move
    Foo(const Foo&) = delete;
    Foo& operator=(const Foo&) = delete;
    Foo(Foo&& other) {
        *this = std::move(other);
    }
    Foo& operator=(Foo&& other) {
        m_pInts = other.m_pInts;
        other.m_pInts = nullptr;
        usedNew = other.usedNew;
        return *this;
    }
};

随着添加数据成员,实现移动变得更加乏味.但是,可移动数据可以放置在单独的struct中,其实例由unique_ptr<>管理.这允许=default用于移动:

Implementing move becomes more tedious as data members are added. However, the moveable data can be placed in a separate struct, an instance of which is managed by unique_ptr<>. This allows =default to be used for move:

class Bar
{
    struct Data
    {
        int* m_pInts;
        bool usedNew;
        // other members ...
    };
    std::unique_ptr<Data> m_pData = std::make_unique<Data>();

public:
    Bar(size_t num, bool useNew = true) {
        m_pData->usedNew = useNew;
        if (m_pData->usedNew)
            m_pData->usedNew = new int[num];
        else
            m_pData->m_pInts = static_cast<int*>(calloc(num, sizeof(int)));
    }
    ~Bar() {
        if (m_pData->usedNew)
            delete[] m_pData->m_pInts;
        else
            free(m_pData->m_pInts);
    }

    // no copy, but move
    Bar(const Bar&) = delete;
    Bar& operator=(const Bar&) = delete;
    Bar(Bar&& other) = default;
    Bar& operator=(Bar&& other) = default;
};

除了unique_ptr<>实例的内存始终在堆上之外,这样的实现还有其他问题吗?

Other than the memory for the unique_ptr<> instance always being on the heap, what other problems exist with an implementation like this?

推荐答案

我的建议是分开关注使用组成.

管理分配的内存的生命周期是智能指针的工作.如何将内存(或其他资源)返回到运行时是智能指针的删除器所关心的.

Managing the lifetime of allocated memory is the job of a smart pointer. How to return that memory (or other resource) to the runtime is the concern of the smart pointer's deleter.

通常,如果您发现自己编写了移动运算符和移动构造函数,那是因为您没有充分分解问题.

In general, if you find yourself writing move operators and move constructors it's because you have not sufficiently decomposed the problem.

示例:

#include <cstring>
#include <memory>

// a deleter
//
struct delete_or_free
{
    void operator()(int* p) const 
    {
      if (free_) {
        std::free(p);
    }
      else {
        delete [] p;
      }
    }

  bool free_;
};


class Foo
{
  //
  // express our memory ownership in terms of a smart pointer.
  //
  using ptr_type = std::unique_ptr<int[], delete_or_free>;
  ptr_type ptr_;

  // other members ...

  //
  // some static helpers (reduces clutter in the constructor)
  //
  static auto generate_new(int size) {
    return ptr_type { new int[size], delete_or_free { false } };
  }

  static auto generate_calloc(int size) {
    return ptr_type { 
      static_cast<int*>(calloc(size, sizeof(int))),
      delete_or_free { true } 
    };
  }

public:

    //
    // our one and only constructor
    //
    Foo(size_t num, bool useNew=true) 
      : ptr_ { useNew ? generate_new(num) : generate_calloc(num) }
    {
    }

    // it's good manners to provide a swap, but not necessary.   
    void swap(Foo& other) noexcept {
      ptr_.swap(other.ptr_);
    }
};

//
// test
//
int main()
{
  auto a = Foo(100, true);
  auto b = Foo(200, false);

  auto c = std::move(a);
  a = std::move(b);
  b = std::move(c);

  std::swap(a, b);
}

这篇关于应该使用unique_ptr来更轻松地实现“移动"语义?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆