Should use unique_ptr to more easily implement “move” semantics?

南笙酒味 提交于 2019-12-04 16:51:39

My advice would be to separate concerns and use composition.

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.

Example:

#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);
}
Barry

Yes. What you're looking for is called the Rule of Zero (as the C++11 extension of the Rule of Three/Five). By having your data all know how to copy and move themselves, the outer class doesn't need to write any of the special member functions. Writing those special members can be error-prone, so not having to write them solves a lot of problems.

So Foo would become just:

class Foo
{
    std::unique_ptr<size_t[]>  data;

public:
    Foo(size_t size): data(new size_t[size]) { }
};

and that's very easy to prove the correctness of.

Yakk - Adam Nevraumont

This is known as the rule of zero.

The rule of zero states that most classes do not implement copy/move assignment/construction or destruction. Instead, you delegate that to resource handling classes.

The rule of 5 states that if you implement any one of the 5 copy/move assign/ctor or dtor, you should implement or delete all 5 of them (or, after due consideration, default them).

In your case, the m_pInts should be a unique pointer, not a raw memory handled buffer. If it is tied to something (say a size), then you should write a pointer-and-size structure that implements the rule of 5. Or you just use a std::vector<int> if the overhead of 3 pointers instead of 2 is acceptable.

Part of this is that you stop invoking new directly. new is an implementation detail in the rule-of-5 types that manage resources directly. Business logic classes do not mess with new. They neither new, nor delete.

unique_ptr is just one of a category of resource-managing types. std::string, std::vector, std::set, shared_ptr, std::future, std::function -- most C++ std types qualify. Writing your own is also a good idea. But when you do, you should strip the resource code from the "business logic".

So if you wrote a std::function<R(Args...)> clone, you'd either use a unique_ptr or a boost::value_ptr to store the function object internal guts. Maybe you'd even write a sbo_value_ptr that sometimes exists on the heap, and sometimes locally.

Then you'd wrap that with the "business logic" of std::function that understands that the thing being pointed to is invokable and the like.

The "business logic" std::function would not implement copy/move assign/ctor, nor a destructor. It would probably =default them explicitly.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!