Каковы некоторые из C ++ 11 std::unique_ptr
использует и подводные камни?
Могу ли я использовать std::unique_ptr
также хранить динамически размещенные массивы?
Могу ли я использовать std::unique_ptr
также с ресурсами, использующими пользовательский механизм удаления?
Давайте организуем некоторые использования и ошибки, используя Q&Формат.
Q1: Я хотел бы хранить указатель в класс Component
внутри моего класса X
,
Я не хочу класс «контейнер» X
быть копируемым; X
является единственным владельцем Component
экземпляров.
я знаю это владеющим сырье указатели являются плохой вещью и потенциальными источниками «утечек» (вместо наблюдения сырые указатели в порядке).
Какие умный указатель Могу ли я использовать для этой цели?
A1: C ++ 11-х std::unique_ptr
это конечно хороший вариант.
Это хорошо в случаях уникальная (не общая) собственность, и не имеет накладных расходов std::shared_ptr
,
Это отличная замена для предыдущего C ++ 98/03 boost::scoped_ptr
,
На самом деле, кроме того, std::unique_ptr
обеспечивает переместить семантику.
Итак, если класс X
содержит unique_ptr<Component>
члены данных (и другие подвижные члены данных), весь класс X
будет автоматически подвижна.
Пример использования показан ниже:
#include <memory> // for std::unique_ptr
class X
{
std::unique_ptr<Component> m_pComponent;
....
public:
X()
: m_pComponent( new Component() )
{
....
}
}
(Конечно, будучи умный указатель, нет необходимости явно удалять его в деструкторе содержащего класса.)
Q2: Замечательно! Нет необходимости в явном деструкторе, нет std::shared_ptr
накладные расходы (типичная философия C ++: «Мы не платим за то, что не используем»), переместить семантику машин уже реализовано!
Однако у меня есть проблема: мой класс Component
имеет перегрузку конструктора, которая принимает некоторый параметр, который мне нужно вычислить в коде конструктора перед созданием Component
экземпляров. Я пытался использовать обычные operator=
Назначение в конструкторе для назначения вновь созданного Component
к unique_ptr
, но я получил сообщение об ошибке:
X::X()
{
....
const int param = CalculateCoolParameter();
// This assignment fails:
m_pComponent = new Component(param); // <---- Error pointing to '=' here
^--- error
}
A2: ОК, возможно, вы ожидали operator=
перегрузка освобождая предыдущее
указатель (если есть) и присвоение вновь созданному.
К сожалению, такой перегрузки нет.
Тем не менее std::unique_ptr::reset()
метод подойдет!
m_pComponent.reset( new Component(param) );
Q3: Привет! это unique_ptr
это действительно круто!
Мне нравится тот факт, что он умный, он автоматически перемещается и не приносит лишних затрат.
Итак, я хотел бы использовать его для хранения динамически распределяемый массив некоторого постоянного размера (рассчитывается во время выполнения) вместо использования std::vector
(в этой части кода я сильно ограничен и не хочу платить за std:vector
накладные расходы, так как я не хочу все std::vector
динамическое изменение размеров, глубокое копирование и т. д.).
Я попробовал что-то вроде этого:
const size_t count = GetComponentsCount();
unique_ptr<Component> components( new Component[count] );
Компилируется нормально, но я заметил, что ~Component
деструктор называется только один раз, вместо этого я ожидал count
деструктор звонит! Что здесь не так?
A3: Проблема в том, что с приведенным выше синтаксисом std::unique_ptr
использования delete
освободить выделенные объекты. Но так как те были распределены с использованием new[]
, правильный вызов очистки delete[]
(не простой delete
без скобок).
Чтобы исправить это и проинструктировать unique_ptr
правильно использовать delete[]
для освобождения ресурсов должен использоваться следующий синтаксис:
unique_ptr<Component[]> components( new Components[count] );
// ^^
//
// Note brackets "[]" after the first occurrence of "Component"// in unique_ptr template argument.
//
Q4: Замечательно! Но могу ли я использовать unique_ptr
также в тех случаях, когда код выпуска ресурса не выполняется с использованием обычного C ++ delete
(или же delete[]
), но вместо этого, используя некоторые пользовательская функция очистки, лайк fclose()
для C <stdio.h>
файлы (открываются с fopen()
), или же CloseHandle()
для файла Win32 HANDLE
s (создано с использованием CreateFile()
)?
A4: Это определенно возможно: вы можете указать пользовательский удалитель за std::unique_ptr
,
например.:
//
// Custom deleter function for FILE*: fclose().
//
std::unique_ptr<FILE, // <-- the wrapped raw pointer type: FILE*
int(*)(FILE*)> // <-- the custom deleter type: fclose() prototype
myFile( fopen("myfile", "rb"), // <-- resource (FILE*) is returned by fopen()
fclose ); // <-- the deleter function: fclose()//
// Custom deleter functor for Win32 HANDLE: calls CloseHandle().
//
struct CloseHandleDeleter
{
// The following pointer typedef is required, since
// the raw resource is HANDLE (not HANDLE*).
typedef HANDLE pointer;
// Custom deleter: calls CloseHandle().
void operator()(HANDLE handle) const
{
CloseHandle(handle);
}
};
std::unique_ptr<HANDLE, CloseHandleDeleter> myFile( CreateFile(....) );
Других решений пока нет …