Я работаю с унаследованным C API, при котором приобретение какого-то ресурса дорого и освобождает этот ресурс абсолютно критично. Я использую C++ 14, и я хочу создать класс для управления этими ресурсами. Вот основной скелет вещи ...C++ Move Semantics - обертывание API Legacy C
class Thing
{
private:
void* _legacy;
public:
void Operation1(...);
int Operation2(...);
string Operation3(...);
private:
Thing(void* legacy) :
_legacy(legacy)
{
}
};
Это не единственный синглетный узор. Ничто не является статичным, и может быть много экземпляров Thing
, которые управляют собственными ресурсами. Кроме того, это не просто умный указатель. Обернутый указатель _legacy
является закрытым, и все операции отображаются через некоторые функции публичных экземпляров, которые скрывают унаследованный API от потребителей.
Конструктор является закрытым, поскольку экземпляры Thing
будут возвращены с статического завода или named-constructor, который фактически приобретет ресурс. Вот дешевая имитация этой фабрики, используя malloc()
в качестве места держателя для кода, который будет ссылаться на устаревшую API ...
public:
static Thing Acquire()
{
// Do many things to acquire the thing via the legacy API
void* legacy = malloc(16);
// Return a constructed thing
return Thing(legacy);
}
Вот деструктор, который отвечает за освобождение унаследованного ресурса, опять же, free()
просто заполнитель ...
~Thing() noexcept
{
if (nullptr != _legacy)
{
// Do many things to free the thing via the legacy API
// (BUT do not throw any exceptions!)
free(_legacy);
_legacy = nullptr;
}
}
Теперь я хочу, чтобы убедиться, что именно один наследство ресурса управляется ровно одним экземпляром Thing
. Я не хотел, чтобы пользователи класса Thing
передавали экземпляры по желанию - они должны либо принадлежать локально к классу или функции, либо напрямую, либо через unique_ptr
, либо обернуты shared_ptr
, которые могут быть переданы. С этой целью я удалил оператор присваивания и скопировал конструкторы ...Либо мне пришлось изменить свой заводский метод, чтобы вернуть unique_ptr<Thing>
или shared_ptr<Thing>
, или мне пришлось реализовать семантику перемещения. Я не хочу, чтобы диктовать шаблон, под которым Thing
следует использовать таким образом, я решил добавить Move-конструктор и перемещения присваивания-оператор следующим образом ...
Thing(Thing&& old) noexcept : _legacy(old._legacy)
{
// Reset the old thing's state to reflect the move
old._legacy = nullptr;
}
Thing& operator= (Thing&& old) noexcept
{
if (&old != this)
{
swap(_legacy, old._legacy);
}
return (*this);
}
С этим все сделано, я мог бы использовать Thing
как местные и переместить его о ...
Thing one = Thing::Acquire();
Thing two = move(one);
Я не мог разорвать шаблон, пытаясь совершить самостоятельно задание:
Thing one = Thing::Acquire();
one = one; // Build error!
I Кл d также сделать unique_ptr
на один ...
auto three = make_unique<Thing>(Thing::Acquire());
Или shared_ptr
...
auto three = make_shared<Thing>(Thing::Acquire());
Все работало, как я ожидал, и мой деструктор побежал точно в нужный момент во всех моих тестах. На самом деле, единственным раздражением было то, что make_unique
и make_shared
как фактически вызывают движение-конструктор - он не был оптимизирован, как я и надеялся.
Первый вопрос: ли я реализовал ход -constructor и перемещения присваивания-оператора правильно? (Они довольно новы для меня, и это будет первый раз, когда я использовал его в гневе.)
Второй вопрос: Прокомментируйте этот шаблон! Это хороший способ обернуть устаревшие ресурсы в классе C++ 14?
И наконец: Должен ли я что-либо изменить, чтобы код был лучше, быстрее, проще или читабельнее?
Вы всегда можете использовать [статический одноэлементный шаблон] (http://stackoverflow.com/questions/1008019/c-singleton-design-pattern), чтобы избежать использования интеллектуальных указателей. – NathanOliver
Это не синглтон - только псевдо-синглтон. Там может быть несколько вещей, все приобретаются отдельно, но управляются экземпляром «Thing» каждый. – Xharlie
Кажется, вы изобретаете умный указатель, а затем сохраняете его в другом умном оверте. Я бы просто написал пользовательский делетер и напрямую использовал 'std :: unique_ptr' или' std :: shared_ptr'. – Galik