2015-03-13 14 views
7

Piszę klasę, która używa dwóch obiektów utworzonych przy użyciu interfejsu C. Obiekty wyglądają następująco:unique_ptr, niestandardowy deleter i Reguła zerowa

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

(podobnie dla bar_t). Ponieważ C++ 11, chcę zawinąć je w inteligentny wskaźnik, więc nie muszę pisać żadnej ze specjalnych metod. Klasa będzie miała wyjątkową własność dwóch obiektów, tak unique_ptr logicznie sensu ... ale nadal musiałby napisać konstruktora:

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

Z drugiej strony, z shared_ptr, nie musiałby napisz konstruktor lub użyj aliasu typu, ponieważ mogłem po prostu przekazać delete_foo do reset() - chociaż to by spowodowało, że mój MyClass mógł być kopiowany i nie chcę tego.

Jaki jest prawidłowy sposób zapisu MyClass przy użyciu semantyki unique_ptr i nadal stosować się do zasady zero?

Odpowiedz

8

Twoja klasa nie musi zadeklarować destruktora (otrzyma poprawną domyślną implementację bez względu na to, czy zadeklarujesz ją jako domyślną), a więc nadal będzie przestrzegać "reguły zerowej".

jednak można poprawić to poprzez obiekty funkcyjne deleters, zamiast wskaźników:

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>>; 

to ma kilka korzyści:

  • unique_ptr nie trzeba przechowywać dodatkowe wskaźnik
  • funkcja kasowania może być wywołana bezpośrednio, a nie przez wskaźnik
  • nie trzeba pisać konstruktora; domyślny konstruktor zrobi to, co trzeba.
+0

Wygląda to na coś, co powinno znajdować się w standardowej bibliotece. – rubenvb

+1

To było świetne. Zrobiłem niewielką odmianę tego, z 'szablonem struct unique_ptr_deleter;' który ma prywatny klasy 'deleter', a następnie' using type = std :: unique_ptr ; '. Zapisuje ilość pisania niezbędnego ... wtedy mogę trzymać się razem ('unique_ptr_deleter_t foo_;') – Barry

Powiązane problemy