Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

unique_ptr, custom deleter, and Rule of Zero

I am writing a class that uses two objects created using a C interface. The objects look like:

typedef struct... foo_t;
foo_t* create_foo(int, double, whatever );
void delete_foo(foo_t* );

(similarly for bar_t). Because C++11, I want to wrap these in a smart pointer so I don't have to write any of the special methods. The class will have unique ownership of the two objects, so unique_ptr logically make sense... but I would still have to write a constructor:

template <typename T>
using unique_ptr_deleter = std::unique_ptr<T, void(*)(T*)>;

struct MyClass {
     unique_ptr_deleter<foo_t> foo_;
     unique_ptr_deleter<bar_t> bar_;

     MyClass()
         : foo_{nullptr, delete_foo}
         , bar_{nullptr, delete_bar}
     { }

     ~MyClass() = default;

     void create(int x, double y, whatever z) {
         foo_.reset(create_foo(x, y, z));
         bar_.reset(create_bar(x, y, z));
};

On the flip side, with shared_ptr, I wouldn't have to write a constructor, or use a type alias, since I could just pass in delete_foo into reset() - although that would make my MyClass copyable and I don't want that.

What is the correct way to write MyClass using unique_ptr semantics and still adhere to Rule of Zero?

like image 399
Barry Avatar asked Mar 13 '15 15:03

Barry


1 Answers

Your class doesn't need to declare a destructor (it will get the correct default implementation whether or not you declare it defaulted), so still obeys the "Rule of Zero".

However, you might improve this by making the deleters function objects, rather than pointers:

template <typename T> struct deleter;
template <> struct deleter<foo_t> {
    void operator()(foo_t * foo){delete_foo(foo);}
};
template <> struct deleter<bar_t> {
    void operator()(bar_t * bar){delete_bar(bar);}
};

template <typename T>
using unique_ptr_deleter = std::unique_ptr<T, deleter<T>>;

This has a few benefits:

  • the unique_ptr doesn't need to store an extra pointer
  • the delete function can be called directly, rather than via a pointer
  • you don't need to write a constructor; the default constructor will do the right thing.
like image 112
Mike Seymour Avatar answered Sep 25 '22 03:09

Mike Seymour