从std::继承类

Inheriting classes from std::

本文关键字:继承 std      更新时间:2023-10-16

有很多类似的问题,我发现使用这种模式既有支持的理由,也有反对的理由,所以我在这里问这个:

我需要在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++专家,但是有什么特别的理由不这样做吗?

有什么特别的理由不这样做吗?

是的。你得到的是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
    };
  ...
}; 

似乎你认为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类的成员。
  • 我不建议继承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.
  };
  ...
};

我认为它实际上是更好的包装它在你自己的类(即代理使用std作为成员),因为它使它更加松散耦合,如果你想使用不同的数据结构,这将是相当容易的,因为你可以简单地修改你自己的类,而std类不应该被修改。如果对象的接口很简单,也许您甚至应该实现自己的接口(当您想要使用与std或boost不同的数据结构或任何适合您需求的数据结构时,使其易于兼容)。您当前的实现可能不是评论中提到的最好的,但我仍然建议在您自己的类中包装std使用(特别是在较大的应用程序中)。