Если я объявляю объект, заключенный в общий указатель:
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-й способ кажется более читаемым, и я не ожидаю увеличения пределов производительности. Я просто хочу убедиться, что в этом нет ничего опасного.
Спасибо.
c++
c++11
shared-ptr
c++-faq
Стив Х
источник
источник
const std::shared_ptr<myClass>& arg1
DoSomething
ли нужно делить собственность? Похоже, вместо этого нужно просто взять ссылку ...void DoSomething(myClass& arg1)
.shared_ptr<>
конкретно, вы должны передать значение, чтобы фактически разделить собственность.std::make_shared
является не только более эффективным, но и безопаснее , чемstd::shared_ptr
конструктор.Ответы:
Конечно, я могу вам помочь. Я предполагаю, что вы имеете некоторое представление о семантике владения в C ++. Это правда?
Хороший.
Хорошо, я могу придумать только две причины, чтобы
shared_ptr
поспорить:shared_ptr
s.Что вас интересует?
Примеры таких функций включают
std::static_pointer_cast
настраиваемые компараторы или предикаты. Например, если вам нужно найти все уникальные shared_ptr из вектора, вам понадобится такой предикат.В яблочко.
Да. И если он не меняет указатель, вы хотите передать по константной ссылке. Нет необходимости копировать, так как вам не нужно делить владение. Это другой сценарий.
Тот, где вы разделяете собственность? ОК. Как вы делите собственность с
shared_ptr
?Тогда функции нужно будет сделать копию
shared_ptr
, правильно?Нет, это пессимизация. Если он передается по ссылке, у функции не будет другого выбора, кроме как сделать копию вручную. Если он передается по значению, компилятор выберет лучший выбор между копией и перемещением и выполнит его автоматически. Итак, переходите по значению.
Функция может просто переместить
shared_ptr
аргумент в свое хранилище. Перемещение ashared_ptr
обходится дешево, потому что не меняет счетчик ссылок.В этом случае
shared_ptr
совершенно не имеет отношения к функции. Если вы хотите манипулировать указателем, возьмите указатель и позвольте вызывающим абонентам выбрать, какую семантику владения они хотят.Применяются обычные правила. Умные указатели ничего не меняют.
Правильно.
Ах, интересный крайний случай. Я не ожидаю, что это будет происходить часто. Но когда это происходит, вы можете либо передать по значению и игнорировать копию, если она вам не нужна, либо передать по ссылке и сделать копию, если она вам нужна.
Если вы находитесь в ситуации, когда это действительно важно, вы можете предоставить две перегрузки: одна принимает ссылку на const lvalue, а другая - ссылку на rvalue. Один копирует, другой двигается. Другой вариант - шаблон функции идеальной пересылки.
источник
shared_ptr<T> ptr;
(т.е. вызываетеvoid f(T&)
с помощьюf(*ptr)
), и объект не переживает вызов. В качестве альтернативы напишите тот, где вы проходите по значениюvoid f(T)
и предупреждению о проблемах.void f(T&)
потоков и включает в себя потоки. Я думаю, что моя проблема в том, что тон вашего ответа препятствует передаче права собственности на shared_ptr, что является наиболее безопасным, интуитивно понятным и наименее сложным способом их использования. Я был бы категорически против того, чтобы когда-либо советовал новичку извлекать ссылку на данные, принадлежащие shared_ptr <>, возможности неправильного использования велики, а польза минимальна.Я думаю, что люди излишне боятся использовать необработанные указатели в качестве параметров функции. Если функция не собирается хранить указатель или иным образом влиять на его время жизни, необработанный указатель работает так же хорошо и представляет собой наименьший общий знаменатель. Рассмотрим, например, как передать a
unique_ptr
в функцию, которая принимает ashared_ptr
в качестве параметра либо по значению, либо по константной ссылке?void DoSomething(myClass * p); DoSomething(myClass_shared_ptr.get()); DoSomething(myClass_unique_ptr.get());
Необработанный указатель в качестве параметра функции не мешает вам использовать интеллектуальные указатели в вызывающем коде там, где это действительно важно.
источник
DoSomething(*a_smart_ptr)
fun(&x)
сfun(x)
. В последнем примереx
может передаваться либо по значению, либо как const ref. Как указано выше, он также позволит вам пройти,nullptr
если вас не интересует результат ...Да, вся идея shared_ptr <> заключается в том, что несколько экземпляров могут содержать один и тот же необработанный указатель, а базовая память будет освобождена только тогда, когда будет уничтожен последний экземпляр shared_ptr <>.
Я бы избегал указателя на shared_ptr <>, поскольку это противоречит цели, поскольку теперь вы снова имеете дело с raw_pointers.
источник
Передача по значению в вашем первом примере безопасна, но есть идиома получше. По возможности передавайте по константной ссылке - я бы сказал «да» даже при работе с интеллектуальными указателями. Ваш второй пример не совсем сломан, но очень
!???
. Глупо, ничего не достичь и частично лишить возможности интеллектуальных указателей и оставить вас в глючном мире боли, когда вы попытаетесь разыменовать и изменить вещи.источник
shared_ptr<>
частности, в том, что вы должны сделать копию, чтобы фактически разделить собственность; если у вас есть ссылка или указатель на объект,shared_ptr<>
то вы ничем не делитесь и подвержены тем же проблемам времени жизни, что и обычная ссылка или указатель.shared_ptr
вызывающего гарантированно переживет вызов функции, поэтому функция безопасна при использованииshared_ptr<> const &
. Если ему нужно сохранить указатель на более позднее время, его нужно будет скопировать (на этом этапе должна быть сделана копия, а не ссылка), но нет необходимости нести расходы на копирование, если вам не нужно делать Это. В этом случае я бы пошел на постоянную ссылку ....shared_ptr
из интерфейса и передатьconst
ссылку plain ( ) на указанный объект.shared_ptr<>
с самого начала? Теперь ваш API - это просто головная боль, поскольку он вынуждает вызывающего абонента бессмысленно изменять семантику времени жизни объекта только для того, чтобы использовать его. Я не вижу в этом ничего стоящего, кроме как сказать: «Технически это ничего не вредит». Я не вижу ничего спорных о « если вам не нужно совместное владение, не использоватьshared_ptr<>
».в своей функции
DoSomething
вы изменяете член данных экземпляра класса,myClass
поэтому вы изменяете управляемый объект (необработанный указатель), а не объект (shared_ptr). Это означает, что в точке возврата этой функции все общие указатели на управляемый необработанный указатель будут видеть свой член данных:myClass::someField
измененный на другое значение.в этом случае вы передаете объект функции с гарантией, что вы не изменяете его (речь идет о shared_ptr, а не о собственном объекте).
Идиома, чтобы выразить это через: const ref, например
void DoSomething(const std::shared_ptr<myClass>& arg)
Точно так же вы заверяете пользователя своей функции, что не добавляете другого владельца в список владельцев необработанного указателя. Однако вы сохранили возможность изменить базовый объект, на который указывает необработанный указатель.
ПРЕДОСТЕРЕЖЕНИЕ: это означает, что если каким-то образом кто-то вызывает
shared_ptr::reset
перед тем, как вы вызываете вашу функцию, и в этот момент это последний shared_ptr, владеющий raw_ptr, то ваш объект будет уничтожен, а ваша функция будет управлять свисающим указателем на уничтоженный объект. ОЧЕНЬ ОПАСНО!!!источник