【问题标题】:Inheriting classes from std::从 std:: 继承类
【发布时间】:2014-06-01 20:02:55
【问题描述】:

有很多类似的问题,我发现了使用这种模式的赞成和反对理由,所以我在这里问这个问题:

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

【问题讨论】:

  • 你不能让一个类使用与其封闭命名空间相同的标识符
  • 首先:将类命名为与其所在的命名空间相同的名称很少是一个好主意。这只会带来麻烦。其次,拥有 has-a 关系并创建一个模板类 JSON&lt;T&gt; 可能是一个更好的主意,其中 T 是您想要反/序列化的类型。
  • 我认为继承数据结构是个坏主意。我已经做了并且后悔了。授予用户的访问权限可能存在问题。
  • 类名和命名空间只是示例。 @πάνταῥεῖ,如果我将其设为 JSON,那么您如何读取 JSON 文件?您需要提前知道您需要的是 JSON 对象还是 JSON 数组并正确声明它。
  • @FlorinAsavoaie 您可能必须声明多个类来处理 JSON 文件...

标签: c++ json c++11


【解决方案1】:

有什么不这样做的具体原因吗?

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

【讨论】:

    【解决方案2】:

    您似乎认为 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】:
      • 我不建议从 STL 继承。

      • 由于性能原因STL 标准容器没有虚拟析构函数,因此您无法对它们进行多态处理。

      • 这意味着无法使用运行时多态性并期望为它们提供适当的析构函数。

      • 从 STL 继承,虽然是完全允许的,但大多数时候表示设计不佳。我建议不要遵循 inherits from 方式,而是 has a 方式:


      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.
        };
        ...
      };
      

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 2019-05-22
          • 1970-01-01
          • 2013-01-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-12-20
          • 1970-01-01
          相关资源
          最近更新 更多