Ответ 1
A
unique_ptr
не будет работать из-заgetDevice()
, правильно?
Нет, не обязательно. Здесь важно определить соответствующую политику собственности для вашего объекта Device
, то есть кто будет владельцем объекта, на который указывает ваш (умный) указатель.
Будет ли это экземпляр объекта Settings
? Будет ли объект Device
автоматически уничтожаться, когда объект Settings
будет уничтожен, или он должен пережить этот объект?
В первом случае std::unique_ptr
- это то, что вам нужно, поскольку оно делает Settings
единственным (единственным) владельцем заостренного объекта и единственным объектом, который несет ответственность за его уничтожение.
В этом предположении getDevice()
должен возвращать простой указатель наблюдения (указатели наблюдения - это указатели, которые не удерживают выделенный объект). Простейшим видом указателя наблюдения является необработанный указатель:
#include <memory>
class Device {
};
class Settings {
std::unique_ptr<Device> device;
public:
Settings(std::unique_ptr<Device> d) {
device = std::move(d);
}
Device* getDevice() {
return device.get();
}
};
int main() {
std::unique_ptr<Device> device(new Device());
Settings settings(std::move(device));
// ...
Device *myDevice = settings.getDevice();
// do something with myDevice...
}
[ ПРИМЕЧАНИЕ 1: Возможно, вам интересно, почему я использую raw-указатели здесь, когда все говорят, что raw-указатели плохие, небезопасные и опасные. На самом деле это ценное предупреждение, но важно поставить его в правильном контексте: raw-указатели плохие при использовании для управления ручным управлением памятью, то есть распределение и освобождение объектов через new
и delete
. При использовании исключительно в качестве средства для достижения ссылочной семантики и прохождения вокруг не владеющих, указателей наблюдения, в исходных указателях нет ничего опасного, кроме, может быть, из-за того, что следует позаботиться о том, чтобы не разыменовать висячий указатель. - КОНЕЦ ПРИМЕЧАНИЕ 1]
[ ПРИМЕЧАНИЕ 2:. Как было отмечено в комментариях, в этом конкретном случае, когда право собственности уникально и, объект, на который всегда гарантированно присутствует (т.е. внутренний член данных Device
никогда не будет nullptr
), функция getDevice()
может (и, возможно, должна) возвращать ссылку, а не указатель. Хотя это верно, я решил вернуть сюда необработанный указатель, потому что я имел в виду, что это короткий ответ, который можно обобщить на случай, когда Device
может быть nullptr
, и показать, что необработанные указатели в порядке, пока один не использует их для ручного управления памятью. - КОНЕЦ ПРИМЕЧАНИЕ 2]
Ситуация радикально отличается, конечно, если ваш Settings
объект должен не иметь эксклюзивное право собственности на устройство. Это может иметь место, например, если уничтожение объекта Settings
не должно означать уничтожение остроконечного объекта Device
.
Это то, что может сказать только вы, как разработчик вашей программы; из примера, который вы предоставляете, мне сложно сказать, так ли это или нет.
Чтобы помочь вам разобраться, вы можете спросить себя, есть ли другие объекты помимо Settings
, которые имеют право сохранить объект Device
в живых, если они содержат указатель на него, а не просто пассивных наблюдателей. Если это действительно так, вам нужна общая политика владения, которую предлагает std::shared_ptr
:
#include <memory>
class Device {
};
class Settings {
std::shared_ptr<Device> device;
public:
Settings(std::shared_ptr<Device> const& d) {
device = d;
}
std::shared_ptr<Device> getDevice() {
return device;
}
};
int main() {
std::shared_ptr<Device> device = std::make_shared<Device>();
Settings settings(device);
// ...
std::shared_ptr<Device> myDevice = settings.getDevice();
// do something with myDevice...
}
Обратите внимание, что weak_ptr
является указателем наблюдения, а не владеющим указателем - другими словами, он не удерживает заостренный объект в живом состоянии, если все остальные указатели на привязку к указанному объекту выходят за рамки.
Преимущество weak_ptr
по сравнению с обычным необработанным указателем состоит в том, что вы можете с уверенностью сказать, является ли weak_ptr
болтающимся или нет (то есть, указывает ли он на действительный объект или если объект, на который был направлен указатель, был уничтожен), Это можно сделать, вызвав функцию expired()
member в объекте weak_ptr
.