RapidJSON 的设计有一个特性, 进行赋值操作时, 不是把源value复制(copy)到目的 value, 而是转移(move)到目的value. 例如
Value a(123);
Value b(456);
b = a; // a becomes a Null value, b becomes number 123.
这样的设计的目的是 为了提高性能. 对于固定大小的JSON类型(Number, True, False, Null), 复制很简单快捷. 而对于可变大小的类型(String, Array, Object), 复制时会产生大量不容易被察觉的开销. 尤其是当我们需要创建一个临时的值, 把它复制给另一个变量, 然后析构它. 若使用正常的复制 语义:
Document d;
Value o(kObjectType);
{
Value contacts(kArrayType);
// Adding elements to contacts array.
// ...
o.AddMember("contacts", contacts, d.GetAllocator(); // deep clone contacts(may be with lots of allocations)
// destruct contact
}
o
需要分配跟contacts
大小一样的缓冲区, 做深度复制, 然后析构contacts
. 这样会产生大量不必要的内存分配/释放 和内存复制. 有一些方案可以避免实质的复制这些数据, 如引用计数, 垃圾回收等等. 为了使RapidJSON简单和快速, 我们选择使用转移语义来进行赋值. 这与std::auto_ptr
类似, 都是在赋值时转移拥有权. 转移比复制简捷地多, 它只需 析构原来的值, 把源值memcpy()
到目的值, 最后再把源值 设为Null类型.
使用转移语义, 上面的例子变成:
Document d;
Value o(kObjectType);
{
Value contacts(kArraryType);
// Adding elements to contacts array.
o.AddMember("contacts", contacts, d.GetAllocator()); // Just memcpy() of contacts itself to the value of new member(16 bytes)
// contacts became Null here. Its destructiong is trivial.
}
转移语义和临时值 有时, 我们想直接构造一个临时变量传给"转移"函数, 如PushBack()
, AddMember()
. 由于临时对象不能直接转化成正常的值引用, 我们可以调用Move()
函数
Value a(kArrayType);
Document::AllocatorType& allocator = document.GetAllocator();
// a.PushBack(Value(42), allocator); // Compiling error
a.PushBack(Value().SetInt(42), allocator); // fluent API
a.PushBack(Value(42).Move(), allocator); // same as above
翻译原文: http://rapidjson.org/md_doc_tutorial.html#MoveSemantics