有许多类似的问题,我发现使用这种模式的原因和反对理由,所以我在这里问这个问题:
我需要在C ++中创建一个JSON实现(假设它有点像家庭作业)。我想这样做:
namespace JSON {
class JSON { };
class object : public JSON, public std::unordered_map<std::string,JSON> { };
class vector : public JSON, public std::vector<JSON> { };
class string : public JSON, public std::string { };
...
};
如果你考虑一下,这一切都有意义。 JSON对象“is-an”unordered_map,JSON向量“is-a”向量,依此类推。只是它们也是JSON值,例如,JSON向量可以包含任何类型的JSON值(对象,向量,字符串等)。你也可以获得很多好处,然后你可以在C ++中“自然地”使用JSON(你可以在json [“mystringlist”]中有一个实际的std :: string向量,json实际上是一个unordered_map)。
我不是真正的C ++专家,但有什么特别的理由不这样做吗?
答案 0 :(得分:2)
有什么特别的理由不这样做吗?
是。你用这个获得的是UB。问题来自std::
容器不支持多态性。它们不是为了继承而设计的(没有虚拟析构函数),这意味着你不能编写正确/安全的析构函数序列。
相反,您的解决方案可能应该如下所示:
namespace XYZ { // <-- cannot have same name as class here
class JSON { };
class object : public JSON {
std::unordered_map<std::string,JSON> values;
public:
JSON& operator[]( const std::string& key );
};
class vector : public JSON {
// same here
};
...
};
答案 1 :(得分:1)
我不建议继承自STL。
由于性能原因 STL标准容器没有虚拟析构函数,因此您无法以多态方式处理它们。
这意味着无法使用运行时多态,并期望为它们提供正确的解析器。
继承STL虽然是完全允许的,但大部分时间都表示糟糕的设计。我建议不要遵循继承自方式,而是有方式:
namespace JSON {
class JSON { };
class object : public JSON
{
std::unordered_map<std::string, JSON> m;
public:
// provide interface to access m.
};
class Vector : public JSON {
std::vector<JSON> v;
public:
// provide interface to access v.
};
...
};
答案 2 :(得分:1)
您似乎认为JSON对象是无序映射,因此它应该从std::unordered_map
继承。我认为你在这里做出了合乎逻辑的跳跃。一个JSON对象绝对是你所描述的“无序地图”的例子,但它真的是std::unordered_map
吗?我不会这么说。要说它是std::unordered_map
表明它应该共享std::unordered_map
的接口,并且应该能够在使用std::unordered_map
的任何地方使用。我建议std::unordered_map
的接口比JSON对象更复杂,更低级别。
除此之外,大多数情况下标准库类不是用作基类(特别是如果它们以多态方式使用的话)。
考虑到这两点,我建议你用标准库组件的术语表示你的JSON类更有意义,但不能用 is-a表示关系。而是使用标准库组件作为JSON类的成员。
答案 3 :(得分:0)
我认为最好将它包装在你自己的类中(即使用std作为成员的代理),因为它使它更加松散耦合,如果你想使用不同的数据结构,那将非常容易,因为您可以简单地修改自己的类,而不应该修改std类。如果对象的界面很简单,也许你甚至应该实现自己的界面(当你想要使用std或boost中的不同数据结构或任何适合你需要的数据时,使其易于兼容)。您当前的实现可能不是评论中提到的最佳实现,但我仍然建议在您自己的类中包装std用法(特别是在较大的应用程序中)。