Передача общих указателей в качестве аргументов

Если я объявляю объект, заключенный в общий указатель:

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;
}

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

Спасибо.

Ответ 1

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

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

Да, я достаточно удобен в этом вопросе.

Хорошо.

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

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

Какую из вас интересует?

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

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

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

Совершенно верно.

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

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

Хорошо, понял. Расскажите о другом сценарии.

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

Скопировав его.

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

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

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

Хорошая точка. Я должен помнить, что "" Хотите скорость? Pass by Value." более часто.

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

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

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

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

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

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

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

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

Right.

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

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

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

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

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

Ответ 2

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

void DoSomething(myClass * p);

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

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

Ответ 3

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

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

Ответ 4

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