Рассмотрим простой класс рукояток, который владеет ресурсом, а также предоставляет копировальную семантику (копический конструктор и присвоение). Например, clone_ptr
может владеть указателем и вызывать clone()
на нем для копирования целей:
template <class T>
class clone_ptr
{
private:
T* ptr;
public:
explicit clone_ptr(T* p = 0) : ptr(p) {}
~clone_ptr() { delete ptr; }
clone_ptr(const clone_ptr& p)
: ptr(p.ptr ? p.ptr->clone() : 0) {}
clone_ptr& operator=(const clone_ptr& p)
{
if (this != &p)
{
T *p = p.ptr ? p.ptr->clone() : 0;
delete ptr;
ptr = p;
}
return *this;
}
clone_ptr(clone_ptr&& p)
: ptr(p.ptr) { p.ptr = 0; }
clone_ptr& operator=(clone_ptr&& p)
{
if(this != &p)
{
std::swap(ptr, p.ptr);
delete p.ptr;
p.ptr = 0;
}
return *this;
}
};
clone_ptr
ожидал, что конструктор копий и семантика уступки, дублируя ресурсы при копировании. Обратите внимание, что копирование построения или назначения clone_ptr
является относительно дорогой операцией:
clone_ptr<Base> p1(new Derived());
clone_ptr<Base> p2 = p1;
clone_ptr
- это код, который вы можете найти в сегодняшних книгах на C++, за исключением части, обозначенной как move semantics
. Эта часть реализована в терминах C++0x rvalue references
. Вы можете найти хорошее введение и учебники по ссылкам на rvalue в этих документах:
Когда источник копии известен как rvalue
(например: временный объект), можно избежать потенциально дорогого клон()
операции с помощью указателя источника пилферинга (никто не заметит!). Конструктор движения выше делает именно это, оставляя r value в по умолчанию построенном состоянии. Оператор перемещения просто делает то же самое освобождение старых ресурсов.
Теперь, когда код пытается скопировать r value clone_ptr
, или если этот код явно дает разрешение считать источник копии r value (с использованием boost::move
), операция будет выполняться гораздо быстрее.
clone_ptr<Base> p1(new Derived());
clone_ptr<Base> p2 = boost::move(p1);
p2 = clone_ptr<Base>(new Derived());
}
Многие аспекты семантики перемещения могут быть эмулированы для компиляторов, не поддерживающих r value references
и Boost.Move предлагает инструменты для этой цели. С Boost.Move мы можем написать clone_ptr
, чтобы он работал как в компиляторах с rvalue-ссылками, так и в тех, кто соответствует C++03. Вам просто нужно следовать этим простым шагам:
- Оставь конструктора копий, как есть.
Давайте посмотрим, как применяется к clone_ptr
:
template <class T>
class clone_ptr
{
private:
BOOST_COPYABLE_AND_MOVABLE(clone_ptr)
T* ptr;
public:
explicit clone_ptr(T* p = 0) : ptr(p) {}
~clone_ptr() { delete ptr; }
clone_ptr(const clone_ptr& p)
: ptr(p.ptr ? p.ptr->clone() : 0) {}
clone_ptr& operator=(BOOST_COPY_ASSIGN_REF(clone_ptr) p)
{
if (this != &p){
T *tmp_p = p.ptr ? p.ptr->clone() : 0;
delete ptr;
ptr = tmp_p;
}
return *this;
}
clone_ptr(BOOST_RV_REF(clone_ptr) p)
: ptr(p.ptr) { p.ptr = 0; }
clone_ptr& operator=(BOOST_RV_REF(clone_ptr) p)
{
if (this != &p){
delete ptr;
ptr = p.ptr;
p.ptr = 0;
}
return *this;
}
};
В этом случае никакой работы не требуется. Копировальный конструктор уже оптимален.