为何传递指针调用Json::Value::append能通过编译?
为什么带
new的Json::Value::append能编译通过,而自定义示例不行? 昨天在团队代码里发现一段使用jsoncpp库的函数:
#include <json/value.h> Json::Value makeList() { Json::Value list(Json::arrayValue); list.append(new Json::Value(true)); return list; }
这段代码因为在append调用中使用new存在内存泄漏,去掉new即可修复。但查看Json::Value::append的参数要求,它仅接受const Json::Value&或Json::Value&&类型,而C++不会自动将指针隐式转换为引用,按此逻辑Json::Value*本应无法匹配参数类型才对。
为验证这个逻辑,我编写了一个简单的测试示例:
#include <iostream> class Foo { public: int x = 4; }; void printValue(const Foo& f) { std::cout << "The value is " << f.x << ".\n"; } void printValue(Foo&& f) { std::cout << "The value is " << f.x << ".\n"; } int main() { printValue(new Foo()); }
该示例确实出现编译错误,错误信息如下:
own-example.cpp:12:5: error: no matching function for call to 'printValue' printValue(new Foo()); ^~~~~~~~~~ own-example.cpp:8:6: note: candidate function not viable: no known conversion from 'Foo *' to 'const Foo' for 1st argument; dereference the argument with * void printValue(const Foo& f) { std::cout << "The value is " << f.x << ".\n"; } ^ own-example.cpp:9:6: note: candidate function not viable: no known conversion from 'Foo *' to 'Foo' for 1st argument; dereference the argument with * void printValue(Foo&& f) { std::cout << "The value is " << f.x << ".\n"; } ^
那为什么调用Json::Value::append的代码能通过编译,而我写的示例却不能?
原因分析
问题的核心在于jsoncpp的Json::Value类定义了一个非explicit的构造函数,允许从Json::Value*隐式转换为Json::Value对象。
具体来说,Json::Value存在类似这样的构造函数签名:
Value( Value* other ); // 非explicit修饰
当你调用list.append(new Json::Value(true))时,编译器会完成两步隐式转换:
- 将
new返回的Json::Value*指针,通过上述构造函数隐式转换为一个临时的Json::Value对象; - 这个临时对象会匹配
append的Json::Value&&(右值引用)参数,从而顺利完成函数调用。
而你自定义的Foo类没有定义接受Foo*的构造函数,编译器找不到合法的转换路径,因此触发编译错误。
另外要注意,这种隐式转换正是内存泄漏的根源:临时Json::Value对象会复制指针指向的内容,之后原指针指向的堆内存无人释放,最终造成泄漏。去掉new直接传递Json::Value(true)临时对象,就能避免这个问题。
内容的提问来源于stack exchange,提问作者Harry Cutts
相关产品推荐
相关产品推荐

