Плохая практика для возврата уникального 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();

мой вопрос состоит из двух частей:

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