Плохая практика для возврата уникального 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> не навязывает какой-либо семантики собственности, и это очень дешево.