Плохая практика для возврата уникального ptr для необработанного указателя, такого как семантика владения?
я написал статический заводской метод, который возвращает новый объект Foobar, заполненный из другого объекта данных. Я недавно был одержим семантикой владения и задаюсь вопросом, передаю ли я правильное сообщение, имея этот заводской метод return a unique_ptr
.
class Foobar {
public:
static unique_ptr<Foobar> factory(DataObject data);
}
мое намерение состоит в том, чтобы сообщить клиентскому коду, что они владеют указателем. Без умного указателя я бы просто вернул Foobar*
. Однако я хотел бы, чтобы эта память была удалена, чтобы избежать потенциального жуки, так unique_ptr
казалось подходящим решением. Если клиент хочет продлить срок службы указателя, они просто называют .release()
как только они получат unique_ptr
.
Foobar* myFoo = Foobar::factory(data).release();
мой вопрос состоит из двух частей:
- передает ли этот подход правильную семантику владения?
- это "плохая практика" вернуть
unique_ptr
вместо сырого указателя?
3 ответов
возвращение std::unique_ptr
от заводского метода просто отлично и должно быть рекомендуемой практикой. Сообщение, которое он передает (IMO):теперь вы являетесь единственным владельцем этого объекта. Кроме того, для вашего удобства, объект знает, как уничтожить себя.
я думаю, что это намного лучше, чем возврат необработанного указателя (где клиент должен помнить, как и если избавиться от этого указателя).
однако я не понимаю вашего прокомментируйте освобождение указателя, чтобы продлить его срок службы. Вообще я редко вижу повода звонить release
на smartpointer, так как я думаю, что указатели всегда должны управляться какой-то структурой RAII (почти единственная ситуация, когда я вызываю release
должен поместить указатель в другую управляющую структуру данных, например unique_ptr
С другим делетером, после того, как я сделал что-то, чтобы гарантировать дополнительную очистку) .
таким образом, клиент может (и должен) просто хранить unique_ptr
куда-то (например, другой unique_ptr
, который был перемещен из возвращенного), пока им нужен объект (или shared_ptr
, если им нужно несколько копий указателя). Поэтому код на стороне клиента должен выглядеть примерно так:
std::unique_ptr<FooBar> myFoo = Foobar::factory(data);
//or:
std::shared_ptr<FooBar> myFoo = Foobar::factory(data);
лично я бы еще добавил typedef
для возвращаемого типа указателя (в данном случае std::unique_ptr<Foobar>
) и или используемый deleter (в данном случае std::default_deleter) для вашего Заводского объекта. Так будет проще, если позже вы решите измените выделение указателя (и поэтому нужен другой метод уничтожения указателя, который будет виден как второй параметр шаблона std::unique_ptr
).
Поэтому я бы сделал что-то вроде этого:
class Foobar {
public:
typedef std::default_deleter<Foobar> deleter;
typedef std::unique_ptr<Foobar, deleter> unique_ptr;
static unique_ptr factory(DataObject data);
}
Foobar::unique_ptr myFoo = Foobar::factory(data);
//or:
std::shared_ptr<Foobar> myFoo = Foobar::factory(data);
на std::unique_ptr
уникально владеет объектом, на который он указывает. Он говорит: "Я владею этим объектом, и никто другой этого не делает."
это именно то, что вы пытаетесь выразить: вы говорите "вызывающий эту функцию: теперь вы единственный владелец этого объекта; делайте с ним, как вам угодно, его жизнь-ваша ответственность."
Он точно передает правильную семантику и так, как я думаю, все фабрики в C++ должны работать:std::unique_ptr<T>
не навязывает какой-либо семантики собственности, и это очень дешево.