2012-05-31 2 views
64

Если я объявляю объект, обернутый в общем указателе:Passing общих указателей в качестве аргументов

std::shared_ptr<myClass> myClassObject(new myClass()); 

то, что я хотел передать его в качестве аргумента методы:

DoSomething(myClassObject); 

//the called method 
void DoSomething(std::shared_ptr<myClass> arg1) 
{ 
    arg1->someField = 4; 
} 

делает выше просто увеличивать счетчик ссылок shared_pt, и все круто? Или он оставляет свисающий указатель?

Вы все еще должны делать это ?:

DoSomething(myClassObject.Get()); 

void DoSomething(std::shared_ptr<myClass>* arg1) 
{ 
    (*arg1)->someField = 4; 
} 

Я считаю, что второй путь может быть более эффективным, поскольку он имеет только скопировать 1 адрес (в отличие от всего смарт-указатель), но 1-й способ кажется более читаемым, и я не ожидаю, что вы снижаете пределы производительности. Я просто хочу убедиться, что в этом нет ничего опасного.

Спасибо.

+13

'const std :: shared_ptr & arg1' –

+3

Второй способ сломан, первый - идиоматический, если вам действительно нужна ваша функция для совместного владения. Но действительно ли 'DoSomething' действительно нужно поделиться собственностью? Похоже, что вместо этого нужно просто взять ссылку ... – ildjarn

+0

Спасибо Чит, это увеличивает счетчик умных указателей? –

ответ

130

Я хочу передать общий указатель на функцию. Можете ли вы мне помочь?

Конечно, я могу вам помочь. Я предполагаю, что у вас есть некоторое понимание семантики владения в C++. Это правда?

Да, я достаточно комфортно отношусь к теме.

Хорошо.

Хорошо, я могу думать только по двум причинам взять shared_ptr аргумент:

  1. Функция хочет разделить право собственности на объект;
  2. Функция выполняет некоторую операцию, которая работает специально на shared_ptr.

Какой вам интересен?

Я ищу общий ответ, поэтому меня действительно интересуют оба. Мне любопытно, что вы имеете в виду в случае № 2.

Примеры таких функций включают в себя std::static_pointer_cast, пользовательские компараторы или предикаты. Например, если вам нужно найти все уникальные shared_ptr из вектора, вам нужен такой предикат.

Ах, когда функции действительно нужно манипулировать самим смарт-указателем.

Точно.

В этом случае, я думаю, мы должны пройти по ссылке.

Да. И если он не меняет указатель, вы хотите передать его по ссылке const. Вам не нужно копировать, так как вам не нужно делиться собственностью. Это другой сценарий.

Хорошо, получилось. Давайте поговорим о другом сценарии.

Тот, в котором вы делите собственность? ОК. Как вы делитесь собственностью с shared_ptr?

путем копирования.

Тогда функция должна будет сделать копию shared_ptr, правильно?

Очевидно. Поэтому я передаю его ссылкой на const и скопирующую локальную переменную?

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

Хороший вопрос. Я должен помнить эту статью «Want Speed? Pass by Value.» чаще.

Подождите, что, если функция хранит shared_ptr в переменной-члене, например? Разве это не сделает лишнюю копию?

Функция может просто переместить аргумент shared_ptr в его хранилище. Перемещение shared_ptr дешево, потому что оно не меняет количество ссылок.

Ах, хорошая идея.

Но я думаю о третьем сценарии: что делать, если вы не хотите манипулировать shared_ptr или не передавать права собственности?

В этом случае shared_ptr совершенно не относится к данной функции. Если вы хотите манипулировать указателем, возьмите указатель и позвольте собеседникам выбрать, какую именно семантику владения они хотят.

И должен ли я использовать указатель по ссылке или по значению?

Применяются обычные правила. Умные указатели ничего не меняют.

Передайте по значению, если я собираюсь скопировать, передать по ссылке, если я хочу избежать копирования.

Право.

Хм. Я думаю, вы забыли еще один сценарий. Что делать, если я хочу поделиться собственностью, но только в зависимости от определенного состояния?

Ах, интересный кромка. Я не ожидаю, что это произойдет часто. Но когда это произойдет, вы можете либо пройти по значению, либо проигнорировать копию, если она вам не нужна, или передать по ссылке и сделать копию, если вам это нужно.

Я рискую одной избыточной копией в первом варианте и потеряю потенциальный ход во втором. Разве я не могу есть пирог и тоже?

Если вы находитесь в ситуации, когда это действительно имеет значение, вы можете предоставить две перегрузки, одну из которых ссылается на константу lvalue, а другую - на ссылку rvalue. Один экземпляр, другой движется. Еще одним вариантом является шаблон идеальной пересылки.

Я думаю, что это охватывает все возможные сценарии. Большое спасибо.

+6

+1, удивительный. Тем не менее, пропавших без вести. : P – Xeo

+13

номинация на тег 'C++ - faq' –

+8

Некоторые образцы кода были бы хорошими – Jon

3

Да, вся идея о shared_ptr <> заключается в том, что несколько экземпляров могут содержать один и тот же необработанный указатель, а базовая память будет освобождена только тогда, когда уничтожается последний экземпляр shared_ptr <>.

Я бы избегал указателя на shared_ptr <>, поскольку это побеждает цель, поскольку вы теперь имеете дело с raw_pointers снова.

2

Передача по значению в вашем первом примере безопасна, но есть более совершенная идиома. Переходите по ссылке const, когда это возможно - я бы сказал, да, даже имея дело с интеллектуальными указателями. Ваш второй пример не совсем сломан, но это очень !???. Глупый, не достигнув ничего и побеждает часть точки умных указателей и собирается оставить вас в ужасном мире боли, когда вы пытаетесь разыгрывать и изменять вещи.

+0

Если вам требуется совместное владение, вы _need_ должны пройти по значению, иначе вы фактически ничего не делите. Если вам не требуется совместное владение, не начинайте с 'shared_ptr <>'. – ildjarn

+0

@ildjarn - Не уверен, согласен ли я с этим. Можете ли вы создать случай, когда передача указателем имеет смысл, а передача по ссылке не в первую очередь? – djechlin

+3

Никто не выступает за прохождение указателя, если вы имеете в виду необработанный указатель. Передача по ссылке, если нет права собственности, безусловно, идиоматична. Дело в том, что в отношении 'shared_ptr <>' конкретно, что вы _must_ делаете копию, чтобы фактически делиться собственностью; если у вас есть ссылка или указатель на 'shared_ptr <>', то вы ничего не делите и подвергаетесь тем же жизненным проблемам, что и нормальная ссылка или указатель. – ildjarn

13

Я думаю, что люди излишне боятся использования исходных указателей в качестве параметров функции. Если функция не собирается хранить указатель или иным образом влияет на его время жизни, необработанный указатель работает так же хорошо и представляет собой самый низкий общий знаменатель. Рассмотрим, например, как вы передадите unique_ptr в функцию, которая принимает значение shared_ptr как параметр, либо по значению, либо по ссылке const?

void DoSomething(myClass * p); 

DoSomething(myClass_shared_ptr.get()); 
DoSomething(myClass_unique_ptr.get()); 

Необработанный указатель как параметр функции не мешает вам использовать интеллектуальные указатели в вызывающем коде, где это действительно имеет значение.

+8

Если вы используете указатель, почему бы просто не использовать ссылку? 'DoSomething (* a_smart_ptr)' – Xeo

+4

@Xeo, вы правы - это было бы еще лучше. Иногда вам нужно разрешить возможность указателя NULL. –

+0

Если у вас есть соглашение об использовании исходных указателей в качестве выходных параметров, проще всего определить код вызова, который может изменить переменная, например: сравните 'fun (& x)' с 'fun (x)'. В последнем примере 'x' может быть передано по значению или как const ref. Он будет, как указано выше, также позволить вам передать 'nullptr'if, что вас не интересует вывод ... –

Смежные вопросы