2013-03-26 4 views
119

У меня возникли проблемы с пониманием использования интеллектуальных указателей как членов класса в 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... 
} 

Предположим, я хотел бы заменить указатели на умные указатели. A 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... 
} 

Это путь? Огромное спасибо!

+4

Это помогает быть предельно ясным в отношении срока службы, владения и возможных нулей. Например, передав 'device' конструктору' settings', вы хотите все еще иметь возможность ссылаться на него в области вызова или только через 'settings'? Если последнее, 'unique_ptr' полезно. Кроме того, есть ли сценарий, в котором возвращаемое значение 'getDevice()' равно 'null'. Если нет, просто верните ссылку. – Keith

+2

Да, 'shared_ptr' верен в 8/10 случаях. Остальные 2/10 разделяются между 'unique_ptr' и' weak_ptr'. Кроме того, 'weak_ptr' обычно используется для разрыва круговых ссылок; Я не уверен, что ваше использование будет считаться правильным. –

+2

Прежде всего, какое владение вы хотите для члена данных 'device'? Вы сначала должны это решить. – juanchopanza

ответ

149

A unique_ptr не работает из-за getDevice(), правильно?

Нет, не обязательно. Здесь важно определить подходящую политику собственности для вашего объекта Device, то есть кто будет владельцем объекта, на который указывает ваш (умный) указатель.

Это будет случай объекта Settings? Будет ли объект Device уничтожен автоматически, когда объект Settings будет уничтожен или он должен пережить этот объект?

В первом случае std::unique_ptr - это то, что вам нужно, так как он является единственным (уникальным) владельцем заостренного объекта и единственным объектом, который несет ответственность за его уничтожение.

В этом предположении 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:Вы можете быть удивлены, почему я использую сырые указатели здесь, когда все твердит, что сырые указатели плохо, небезопасны, и опасно. На самом деле, это драгоценное предупреждение, но важно поставить его в правильном контексте: необработанные указатели являются плохими при использовании для управления ручным управлением памятью, то есть распределения и освобождения объектов через new и delete.При использовании исключительно в качестве средства для достижения ссылочной семантики и прохождения вокруг не владеющих, указателей наблюдения, в исходных указателях нет ничего опасного, кроме, может быть, из-за того, что следует позаботиться о том, чтобы не разыменовать висячий указатель. - КОНЕЦ ПРИМЕЧАНИЯ 1]

[Примечание 2:Как выяснилось, в комментариях, в данном конкретном случае, когда собственность является уникальным и принадлежащих ему объект всегда гарантированно присутствует (т.е. внутренний элемент данных device никогда не будет nullptr), функция getDevice() может (и, возможно, должна) вернуть ссылку, а не указатель. Хотя это верно, я решил вернуть сюда необработанный указатель, потому что я имел в виду, что это короткий ответ, который можно обобщить на случай, когда device может быть nullptr, и показать, что необработанные указатели в порядке, если не использовать их для ручного управления памятью. - END Примечание 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() объекта weak_ptr.

+0

Thx много, думая о времени разрушения, именно то, что помогло мне понять разные в использовании. Также примечание, в котором рассматривается исходный указатель, очень полезно. Если я правильно ее понимаю, то здесь будет возможно и «weak_ptr» (так как оба наблюдают указатели), но не обязательно? – michaelk

+3

@LKK: Да, правильно. «Weak_ptr» всегда является альтернативой необработанным указателям наблюдения. В каком-то смысле это безопаснее, потому что вы можете проверить, висит ли он перед разыгрыванием, но также с некоторыми накладными расходами. Если вы можете легко гарантировать, что вы не собираетесь разыгрывать висячий указатель, то вам должно быть хорошо наблюдать за необработанными указателями –

+1

@LKK, возможно, самый простой способ убедиться, что вы не будете разыгрывать висячий указатель, чтобы не хранить его , или передать его тому, кто будет. Конечно, вы также должны убедиться, что владелец не будет уничтожен во время текущей области, но это легче увидеть, так как он локальный. Однако это сложнее в многопоточной среде. – enobayram

0
class Device { 
}; 

class Settings { 
    std::shared_ptr<Device> device; 
public: 
    Settings(const std::shared_ptr<Device>& device) : device(device) { 

    } 

    const std::shared_ptr<Device>& getDevice() { 
     return device; 
    } 
}; 

int main() 
{ 
    std::shared_ptr<Device> device(new Device()); 
    Settings settings(device); 
    // ... 
    std::shared_ptr<Device> myDevice(settings.getDevice()); 
    // do something with myDevice... 
    return 0; 
} 

week_ptr используется только для эталонных петель. Граф зависимости должен быть графом с ациклическим направлением. В общих указателях имеется 2 подсчета ссылок: 1 для shared_ptr s и 1 для всех указателей (shared_ptr и weak_ptr). Когда все shared_ptr s удалены, указатель удаляется. Когда требуется указатель от weak_ptr, lock следует использовать для получения указателя, если он существует.

+0

Итак, если я правильно понимаю ваш ответ, умные указатели заменяют необработанные указатели, но не обязательно ссылки? – michaelk

+0

LKK: Да. Правда. – Naszta

+0

Есть ли на самом деле * два * подсчета ссылок в 'shared_ptr'? Не могли бы вы объяснить, почему? Насколько я понимаю, 'weak_ptr' не нужно учитывать, потому что он просто создает новый' shared_ptr' при работе над объектом (если базовый объект все еще существует). –

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