dma*_*ola 5 c++ performance design-patterns rapidjson
在我们的项目中发现了一个神秘的问题后,我仍感到震惊.
我们意识到调用HasMember("string")正在进行额外搜索.因此,出于性能原因,我们会对其进行更改.
主要想法是:
而不是调用HasMember,然后预先缓存参考,如:
rapidjson::Document d;
d.Parse<0>(json);
if(d.HasMember("foo"))
{
const rapidjson::Value& fooValue = d["foo"];
// do something with fooValue
}
Run Code Online (Sandbox Code Playgroud)
变成:
rapidjson::Document d;
d.Parse<0>(json);
const rapidjson::Value& fooValue = d["foo"];
if( !fooValue.IsNull() )
{
// do something with fooValue
}
Run Code Online (Sandbox Code Playgroud)
这非常好,我们保存以执行两次搜索,而不是只执行一次.但是,这就是问题所在.
如果您开始查看rapidjson如何实现nullvalue(在查找失败时默认返回),您将看到以下代码:
//! Get the value associated with the object's name.
GenericValue & operator[](const Ch* name) {
// Check
if (Member * member = FindMember(name)) {
return member->value;
} else {
// Nothing
static GenericValue NullValue;
return NullValue;
}
}
// Finder
const GenericValue & operator[] (const Ch* name) const {
// Return
return const_cast<GenericValue &> (* this)[name];
}
Run Code Online (Sandbox Code Playgroud)
所以,如果没有找到该成员,我们返回一个本地静态变量.乍一看这听起来不错,但由于这是通过引用返回可以很容易地导致隐藏的错误.
想象一下有人改变了静态NullValue的引用.这将导致对IsNull的所有进一步调用(在查找之后)将失败,因为NullValue已更改为其他类型甚至是随机内存.
那么,你是做什么的?你认为这是一个很好的空模式的例子吗?
我很困惑,我喜欢返回默认空值的想法,但因为不作为const返回,这是危险的.而且,即使如果我们确实在所有情况下都将它作为const返回,开发人员仍然可以使用const_cast(但我不希望它,如果他们这样做,将由他们负责).
我想听听其他案例和这样的例子.如果有人可以在rapidjson代码下提供真正的解决方案,那将基本上令人敬畏和惊人.
很久以前社区已经提出了这种设计的缺陷.由于operator[]还需要非const版本,因此无法维护静态变量的完整性.
因此,在新版本的RapidJSON中已更改此API.在operator[]简单地断言非存在密钥.如果不确定密钥是否存在,则最好使用密钥
MemberIterator FindMember(const Ch* name);
ConstMemberIterator FindMember(const Ch* name) const;
Run Code Online (Sandbox Code Playgroud)
并比较值MemberEnd()以检查密钥是否存在.这也记录在这里.
此外,请注意RapidJSON已移至GitHub.许多问题已得到解决.如果可能,请使用最新版本.谢谢.
PS我是RapidJSON的作者.