scoped_ptr очень хорош для этой цели. Но нужно понимать его семантику. Вы можете группировать умные указатели, используя два основных свойства:
- Копируемый: Интеллектуальный указатель может быть скопирован: Копия и исходный общий ресурс.
- Подвижный: умный указатель может быть перемещен: у результата перемещения будет владение, у оригинала больше не будет.
Это довольно распространенная терминология. Для умных указателей существует специальная терминология, которая лучше отмечает эти свойства:
- Передача права собственности: умный указатель - Подвижный
- Доля владения: интеллектуальный указатель можно копировать. Если интеллектуальный указатель уже копируемый, то легко поддерживать семантику передачи права собственности: тогда это просто атомарная операция копирование и сброс оригинала , ограничивающая это интеллектуальными указателями определенных видов (например, только временные умные указатели).
Давайте сгруппируем доступные умные указатели, используя (C)opyable
и (M)ovable
, (N)either
:
boost::scoped_ptr
: N
std::auto_ptr
: M
boost::shared_ptr
: C
auto_ptr
имеет одну большую проблему в том, что он реализует концепцию Movable, используя конструктор копирования. Это связано с тем, что когда auto_ptr был принят в C ++, еще не было способа изначально поддерживать семантику перемещения с помощью конструктора перемещения, в отличие от нового стандарта C ++. То есть вы можете сделать следующее с auto_ptr, и оно работает:
auto_ptr<int> a(new int), b;
// oops, after this, a is reset. But a copy was desired!
// it does the copy&reset-of-original, but it's not restricted to only temporary
// auto_ptrs (so, not to ones that are returned from functions, for example).
b = a;
В любом случае, как мы видим, в вашем случае вы не сможете передать право собственности на другой объект: ваш объект фактически не подлежит копированию. А в следующем стандарте C ++ он будет неподвижным, если вы останетесь с scoped_ptr.
Для реализации вашего класса с помощью scoped_ptr, обратите внимание, что вы удовлетворяете одному из следующих двух пунктов:
- Написать деструктор (даже если он пустой) в файл .cpp вашего класса или
- Сделать
Owned
полностью определенным классом.
В противном случае, когда вы создадите объект Example, компилятор неявно определит для вас деструктор, который вызовет деструктор scoped_ptr:
~Example() { ptr.~scoped_ptr<Owned>(); }
Затем будет вызван вызов scoped_ptr boost::checked_delete
, который будет жаловаться на то, что Owned
будет неполным, если вы не выполнили ни одного из двух вышеуказанных пунктов. Если вы определили свой собственный dtor в файле .cpp, неявный вызов деструктора scoped_ptr будет сделан из файла .cpp, в который вы можете поместить определение вашего Owned
класса.
У вас та же проблема с auto_ptr, но у вас есть еще одна проблема: предоставление auto_ptr с неполным типом в настоящее время является неопределенным поведением (возможно, оно будет исправлено для следующей версии C ++). Итак, когда вы используете auto_ptr, у вас есть , чтобы сделать Owned полным типом в вашем заголовочном файле.
shared_ptr не имеет этой проблемы, потому что он использует полиморфное средство удаления, которое выполняет косвенный вызов удаления. Таким образом, функция удаления создается не в момент создания деструктора, а в момент создания средства удаления в конструкторе shared_ptr.