Неправильно ли возвращать unique_ptr для необработанного указателя, такого как семантика владения? - PullRequest
49 голосов
/ 04 января 2012

Я написал статический метод фабрики, который возвращает новый объект Foobar, заполненный из другого объекта данных. Недавно я был одержим семантикой владения, и мне интересно, передаю ли я правильное сообщение, когда этот фабричный метод возвращает 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 ]

60 голосов
/ 04 января 2012

Возврат 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>) и / или используемого средства удаления (в этом случае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);
17 голосов
/ 04 января 2012

A std::unique_ptr однозначно владеет объектом, на который он указывает. Там написано: «Я владею этим объектом, а никто другой не владеет».

Это именно то, что вы пытаетесь выразить: вы говорите: «Вызывающий эту функцию: теперь вы единственный владелец этого объекта; делайте с ним, как вам угодно, его жизнь - ваша ответственность».

6 голосов
/ 04 января 2012

Это точно передает правильную семантику, и я думаю, что все фабрики в C ++ должны работать: std::unique_ptr<T> не навязывает какую-либо семантику владения, и это очень дешево.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...