Move semantics of RapidJSON

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

Licensed under CC BY-NC-SA 4.0
Built with Hugo
主题 StackJimmy 设计