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?
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:
unique_ptr
doesn't need to store an extra pointer