Как передать интеллектуальные указатели в функции?

При передаче объектов в функции применяются те же правила к интеллектуальным указателям, что и к другим объектам, которые содержат динамическую память?

Когда я передаю, например, std::vector<std::string> в функцию, я всегда рассматриваю следующие параметры:

  • Я собираюсь изменить состояние векторного объекта, но я не хочу, чтобы эти изменения отражались после завершения функции, AKA создает копию.

    void function(std::vector<std::string> vec);
    
  • Я собираюсь изменить состояние векторного объекта, и я do хочу, чтобы эти изменения отражались после завершения функции, AKA делает ссылку.

    void function(std::vector<std::string> & vec);
    
  • Этот объект довольно большой, поэтому мне лучше передать ссылку, но скажите компилятору, чтобы он не менял.

    void function(std::vector<std::string> const& vec);  
    

Теперь это та же логика с умными указателями? И когда я должен рассматривать семантику перемещения? Некоторые рекомендации о том, как я должен передавать интеллектуальные указатели, - это то, что я больше всего желаю.

Ответ 1

Умные указатели имеют семантику указателя, а не семантику значений (ну, не так, как вы это понимаете). Подумайте о shared_ptr<T> как T*; рассматривать его как таковое (ну, за исключением подсчета ссылок и автоматического удаления). Копирование умного указателя не копирует объект, на который он указывает, так же как и копирование T* не копирует T, на который указывает.

Вы не можете скопировать unique_ptr вообще. Вся суть класса заключается в том, что его нельзя скопировать; если бы это было возможно, то это не было бы уникальным (то есть: единственным) указателем на объект. Вы должны либо передать его по какой-либо форме, либо перемещая его.

Умные указатели - это все, на что они указывают. Кто владеет этой памятью и кто будет нести ответственность за ее удаление. unique_ptr представляет собой уникальное право собственности: именно одна часть кода владеет этой памятью. Вы можете передать право собственности (через move), но при этом вы потеряете право собственности на память. shared_ptr представляет совместное владение.

Во всех случаях использование интеллектуального указателя в списке параметров означает передачу права собственности. Поэтому, если функция принимает интеллектуальный указатель, то она собирается заявить право собственности на этот объект. Если функция не должна брать на себя ответственность, тогда она не должна воспринимать умный указатель; используйте голый указатель (T*) или ссылку (T&) и не храните его.

Если вы передаете кому-то unique_ptr, вы даете им право собственности. Это означает, что по характеру уникальной собственности вы теряете право собственности на память. Таким образом, почти нет причин когда-либо пропускать unique_ptr на что угодно, кроме как по значению.

Аналогичным образом, если вы хотите передать права собственности на какой-либо объект, вы передаете shared_ptr. Независимо от того, делаете ли вы это по ссылке или по стоимости, зависит от вас. Поскольку вы делитесь собственностью, она все равно будет делать копию (предположительно), поэтому вы можете также взять ее по стоимости. Функция может использовать std::move, чтобы переместить ее в члены класса или тому подобное.

Ответ 2

Умный указатель - это объект, который ссылается на другой объект, управляющий его временем жизни.

Передача интеллектуального указателя требует уважения к семантике поддержки smart poitner:

  • Передача const smartptr<T>& всегда работает (и вы не можете изменить указатель, но можете изменить состояние того, на что оно указывает).
  • Передача smartptr<T>& всегда работает (и вы также можете изменить указатель).
  • Передача как smartptr<T> (путем копирования) работает только в том случае, если smartptr является копируемым. Он работает с std::shared_ptr, но не с std::unique_ptr, если только вы не "переместите" его на вызов, как в func(atd::move(myptr)), тем самым сбрасывая myptr, перемещая указатель на переданный параметр. (Обратите внимание, что перемещение неявно, если myptr является временным).
  • Передача как smartptr<T>&& (путем перемещения) налагает указатель на перемещение по вызову, заставляя вас явно использовать std::move (но требует "переместить", чтобы иметь смысл для конкретного указателя).

Ответ 3

Если функция не собирается изменять или копировать указатель, просто используйте вместо этого указатель немой. Умные указатели используются для управления временем жизни объекта, но функция не изменяет время жизни, поэтому ему не нужен умный указатель, а использование немого указателя дает вам некоторую гибкость в типе, используемом вызывающим.

void function(std::string * ptr);

function(my_unique_ptr.get());
function(my_shared_ptr.get());
function(my_dumb_ptr);

unique_ptr невозможно скопировать, поэтому, если вы должны передать его, вы должны передать ссылку.