Преимущество использования std::unique_ptr<T>
(кроме не запоминая называть delete
или delete[]
явно) является то, что он гарантирует, что указатель либо nullptr
или он указывает на действительный экземпляр (базового) объекта.Я вернусь к этому после того, как я отвечу на ваш вопрос, но первое сообщение DO использует интеллектуальные указатели для управления временем жизни динамически распределенных объектов.
Теперь ваша проблема на самом деле , как использовать это с вашим старым кодом.
Мое предложение состоит в том, что если вы не хотите передавать или передавать права собственности, вы должны всегда передавать ссылки на объект. Объявите вашу функцию, как это (с или без const
классификаторов, при необходимости):
bool func(BaseClass& ref, int other_arg) { ... }
Тогда вызывающего абонент, который имеет std::shared_ptr<BaseClass> ptr
либо обрабатывать nullptr
дела или он попросит bool func(...)
вычислить результат:
if (ptr) {
result = func(*ptr, some_int);
} else {
/* the object was, for some reason, either not created or destroyed */
}
Это означает, что любой вызывающий абонент должен пообещать, что ссылка действительна и что она будет оставаться действительной во время выполнения тела функции.
Вот причина, почему я твердо верю, вы должны не передача сырья указатели или ссылки на смарт-указатели.
Необработанный указатель - это только адрес памяти. Может иметь одно из (не менее) 4 значений:
- Адрес блока памяти, где расположен желаемый объект. (good)
- Адрес 0x0, который вы можете быть уверенным, не является неразрешимым и может иметь семантику «ничего» или «нет объекта». (the bad)
- Адрес блока памяти, который находится за пределами адресного пространства вашего процесса (разыменование его, мы надеемся, приведет к сбою вашей программы). (уродливый)
- Адрес блока памяти, который может быть разыменован, но который не содержит того, что вы ожидаете. Возможно, указатель был случайно изменен, и теперь он указывает на другой доступный для записи адрес (полностью другой переменной в вашем процессе). Запись в это место памяти вызовет массу удовольствия, иногда, во время исполнения, потому что ОС не будет жаловаться, пока вам разрешено писать там. (Zoinks!)
Правильно используя смарт-указатели смягчают, а страшные случаи 3 и 4, которые, как правило, не обнаруживаются во время компиляции и которые вы обычно только опытом во время выполнения, когда ваша программа падает или делают неожиданные вещи.
Передача смарт-указатели в качестве аргументов имеет два недостатка: вы не можете изменить const
-ness в указал объект без создания копии (которая добавляет накладные расходы на shared_ptr
и не представляется возможным для unique_ptr
), и вы по-прежнему остается второй (nullptr
) смысл.
Я обозначил второй случай как (плохой) с точки зрения дизайна. Это более тонкий аргумент в отношении ответственности.
Представьте, что это означает, когда функция принимает nullptr
в качестве параметра. Сначала нужно решить, что с ним делать: использовать «магическое» значение вместо недостающего объекта? полностью изменить поведение и вычислить что-то еще (что не требует объекта)? паника и исключение? Более того, что происходит, когда функция принимает 2, или 3 или даже больше аргументов с помощью необработанного указателя? Он должен проверять каждый из них и соответствующим образом адаптировать свое поведение. Это добавляет совершенно новый уровень поверх проверки ввода без какой-либо реальной причины.
Вызывающий должен быть единственным, у которого достаточно контекстной информации для принятия этих решений, или, другими словами, Плохой менее страшноват, чем больше вы знаете. С другой стороны, функция должна просто принять обещание звонившего, что память, на которую она указана, безопасна для работы в соответствии с назначением. (Ссылки по-прежнему являются адресами памяти, но концептуально представляют собой обещание действительности.)
Это хорошо, но вы можете избавиться от 'std :: unique_ptr' для' 'std :: vector' аргумента? –