У меня проблемы с пониманием использования умных указателей в качестве членов класса в C ++ 11. Я много читал об умных указателях и думаю, что понимаю, как unique_ptr
и shared_ptr
/ или weak_ptr
работаю в целом. То, что я не понимаю, является реальным использованием. Кажется, что все рекомендуют использовать unique_ptr
как способ идти почти все время. Но как бы я реализовать что-то вроде этого:
class Device {
};
class Settings {
Device *device;
public:
Settings(Device *device) {
this->device = device;
}
Device *getDevice() {
return device;
}
};
int main() {
Device *device = new Device();
Settings settings(device);
// ...
Device *myDevice = settings.getDevice();
// do something with myDevice...
}
Допустим, я хотел бы заменить указатели умными указателями. А unique_ptr
не будет работать из-за getDevice()
, верно? Так что это время, когда я использую shared_ptr
и weak_ptr
? Нет способа использования unique_ptr
? Мне кажется, что в большинстве случаев shared_ptr
имеет больше смысла, если я не использую указатель в очень маленькой области видимости?
class Device {
};
class Settings {
std::shared_ptr<Device> device;
public:
Settings(std::shared_ptr<Device> device) {
this->device = device;
}
std::weak_ptr<Device> getDevice() {
return device;
}
};
int main() {
std::shared_ptr<Device> device(new Device());
Settings settings(device);
// ...
std::weak_ptr<Device> myDevice = settings.getDevice();
// do something with myDevice...
}
Это путь? Огромное спасибо!
источник
device
к конструкторуsettings
, хотите ли вы по-прежнему иметь возможность ссылаться на него в области вызова или только черезsettings
? Если последнее,unique_ptr
полезно. Кроме того , у вас есть сценарий , в котором возвращаемое значениеgetDevice()
являетсяnull
. Если нет, просто верните ссылку.shared_ptr
правильно в 8/10 случаях. Остальные 2/10 разделены междуunique_ptr
иweak_ptr
. Кроме того,weak_ptr
обычно используется для разрыва циклических ссылок; Я не уверен, что ваше использование будет считаться правильным.device
члена данных? Сначала вы должны решить это.unique_ptr
вместо этого и отказаться от владения при вызове конструктора, если я знаю, что он мне больше не нужен. Но, как дизайнерSettings
класса, я не знаю, хочет ли звонящий сохранить ссылку. Возможно, устройство будет использоваться во многих местах. Хорошо, возможно, это именно твоя точка зрения. В этом случае я не был бы единственным владельцем, и я думаю, что именно тогда я бы использовал shared_ptr. И так: умные точки заменяют указатели, а не ссылки, верно?Ответы:
Нет, не обязательно Здесь важно определить подходящую политику владения для вашего
Device
объекта, т.е. кто будет владельцем объекта, на который указывает ваш (умный) указатель.Это собирается быть экземпляр
Settings
объекта в одиночку ? ПридетсяDevice
ли уничтожатьSettings
объект автоматически при его разрушении или он переживет этот объект?В первом случае
std::unique_ptr
это то , что вам нужно, так как он делаетSettings
единственным (уникальным) владельцем указанного объекта и единственным объектом, который отвечает за его уничтожение.В соответствии с этим предположением
getDevice()
следует возвращать простой указатель наблюдения (указатели наблюдения - это указатели, которые не поддерживают указанный объект в живых). Самым простым видом указателя наблюдения является необработанный указатель:[ ПРИМЕЧАНИЕ 1: Вам может быть интересно, почему я здесь использую сырые указатели, когда все говорят, что необработанные указатели плохие, небезопасные и опасные. На самом деле, это ценное предупреждение, но важно поместить его в правильный контекст: необработанные указатели плохи, когда используются для ручного управления памятью , то есть для выделения и освобождения объектов через
new
иdelete
. При использовании исключительно в качестве средства для достижения ссылочной семантики и обхода несобственников при наблюдении указателей в исходных указателях нет ничего опасного, за исключением, возможно, того факта, что следует избегать разыменования висящего указателя. - КОНЕЦ ПРИМЕЧАНИЕ 1 ][ ПРИМЕЧАНИЕ 2: Как выяснилось в комментариях, в этом конкретном случае, когда право собственности является уникальным и всегда гарантированно присутствует принадлежащий объект (т. Е. Внутренний элемент данных
device
никогда не будетnullptr
), функцияgetDevice()
может (и, возможно, должна) вернуть ссылку, а не указатель. Хотя это и правда, я решил вернуть здесь необработанный указатель, потому что я имел в виду, что это будет краткий ответ, который можно обобщить на случай, где этоdevice
возможноnullptr
, и показать, что необработанные указатели в порядке, если их не использовать для ручное управление памятью. - КОНЕЦ ПРИМЕЧАНИЕ 2 ]Конечно, ситуация радикально отличается, если ваш
Settings
объект не должен иметь исключительное право собственности на устройство. Это может иметь место, например, если разрушениеSettings
объекта не должно также подразумевать разрушение заостренногоDevice
объекта.Это то, что может сказать только вы, как разработчик вашей программы; из приведенного вами примера мне трудно сказать, так ли это на самом деле.
Чтобы помочь вам понять это, вы можете спросить себя, есть ли какие-либо другие объекты, кроме тех,
Settings
которые имеют право поддерживатьDevice
объект в течение всего времени, пока он удерживает указатель на него, вместо того, чтобы быть просто пассивными наблюдателями. Если это действительно так, то вам нужна политика совместного владения , котораяstd::shared_ptr
предлагает следующее:Обратите внимание, что
weak_ptr
это указатель- наблюдатель , а не указатель-владелец - другими словами, он не поддерживает указательный объект живым, если все другие указатели на указанный объект выходят из области видимости.Преимущество
weak_ptr
над регулярным сырым указателем является то , что вы можете смело сказать , является лиweak_ptr
это оборванный или нет (то есть ли это указует на действительный объект, или если объект , первоначально указал было разрушено). Это можно сделать, вызвавexpired()
функцию-членweak_ptr
объекта.источник
weak_ptr
всегда является альтернативой необработанным указателям наблюдения. В некотором смысле это безопаснее, потому что вы можете проверить, не свисает ли оно, прежде чем разыменовывать его, но оно также связано с некоторыми накладными расходами. Если вы можете легко гарантировать, что вы не собираетесь разыменовывать висящий указатель, то вы должны хорошо соблюдать необработанные указателиgetDevice()
вернуть ссылку, не так ли? Таким образом, абонент не должен был бы проверитьnullptr
.auto myDevice = settings.getDevice()
создаст новый экземплярDevice
вызываемого типаmyDevice
и создаст его копию из того, на который ссылаетсяgetDevice()
возвращаемая ссылка . Если вы хотитеmyDevice
быть ссылкой, вам нужно сделатьauto& myDevice = settings.getDevice()
. Так что, если я что-то упустил, мы вернулись в той же ситуации, что и без использованияauto
.unique_ptr
клиенту открывает возможность того, что клиент от него уйдет, приобретая таким образом право собственности и оставляя вас с нулевым (уникальным) указателем.const_cast
ею), я лично не стал бы этого делать. Он раскрывает детали реализации, то есть тот факт, что право собственности является уникальным и реализуется черезunique_ptr
. Я вижу вещи таким образом: если вы хотите / должны передать / вернуть право собственности, передайте / верните умный указатель (unique_ptr
илиshared_ptr
, в зависимости от вида собственности). Если вы не хотите / не должны передавать / возвращать владельца, используйтеconst
указатель или ссылку (правильно определенную), в основном в зависимости от того, может ли аргумент быть нулевым или нет.week_ptr
используется только для эталонных циклов. Граф зависимостей должен быть ациклически ориентированным графом. В общих указателях есть 2 счетчика ссылок: 1 дляshared_ptr
s и 1 для всех указателей (shared_ptr
иweak_ptr
). Когда всеshared_ptr
s удалены, указатель удаляется. Когда указатель необходим отweak_ptr
,lock
должен использоваться для получения указателя, если он существует.источник
shared_ptr
? Не могли бы вы объяснить, почему? Насколько я понимаю,weak_ptr
не нужно считать, потому что он просто создает новоеshared_ptr
при работе с объектом (если базовый объект все еще существует).lock
. Надежная версия также является поточно-ориентированной при подсчете ссылок (delete
вызывается только один раз).weak_ptr::lock()
чтобы определить, истек ли срок действия объекта, он должен проверить «блок управления», содержащий первый счетчик ссылок и указатель на объект, поэтому блок управления не должен быть уничтожен, пока всеweak_ptr
еще используются какие-либо объекты, таким образом, числоweak_ptr
объектов должно отслеживаться, что и делает второй счетчик ссылок. Объект разрушается, когда число первых ссылок падает до нуля, блок управления разрушается, когда количество вторых ссылок падает до нуля.