【问题标题】:c++ wrapping types for semantic用于语义的 c++ 包装类型
【发布时间】:2013-12-09 01:32:06
【问题描述】:

自从我上次使用c++已经很久了,从java和python回来,我有一个关于c++良好实践的问题:

我想保留一些关于一些非常简单的对象的语义代码,假设我们有对象 Tag 和 File,它们都只是 std::string 和一个包含多个使用标签和文件的函数的类 TagManager。

我的问题是,创建一个代表这些琐碎对象的自定义类型还是直接使用它们更好?

更具体地说,我可以对函数有以下定义之一:

TagIterable myFunction(Tag tag, File file);
std::vector<Tag> myFunction(Tag tag, File file);
std::vector<std::string> myFunction(std::string tag, std::string file);

我更喜欢第一种解决方案,因为我喜欢保留语义代码,另一方面,这样做需要用户检查这些类型是什么,因此降低了使用的简单性。

我还阅读了另一个问题 (Thou shalt not inherit from std::vector) '不要为了让某些东西看起来更好而生产新实体。'

那么在这种情况下你会怎么做; C++ 哲学建议做什么?

【问题讨论】:

  • File 代表一个实际的文件?或者这是否意味着它是一个文件名?然后最好将其命名为文件名,因为它可能会误导其他人使用您的代码...

标签: c++


【解决方案1】:

我通常在这种情况下使用typedefs:

  • 即使 typedefs 本身没有引入 std::vector&lt;&gt; 的新类型或讨厌的派生词,对于使用您的代码的人来说,别名仍然可能是烦人的间接方式。
  • 另一方面,它们可以很好地公开和记录(提供一个示例),以便每个人从一开始就使用它们。
  • 它们有助于避免构建破坏者,以防需要更改类型(前提是在您的具体情况下,不使用或在替换中提供了特殊的 std::string 功能)。
  • 因此,如果需要,稍后可以将 typedef 更改为具有额外属性和功能的新类的别名,或者只是为了类型安全。
  • 它们允许轻松跟踪使用位置,以及在大多数 IDE 中进行重构。
namespace mylib
{
    typedef std::string Tag; //perhaps 'TagType' or 'tag_type', to follow the naming
                             //conventions of the standard library, e.g.
                             //std::vector::value_type
    typedef std::string File;//Same as above, 'FileName' might be more appropriate

    typedef std::vector<Tag> TagIterable;
                             //Could be named 'TagContainer',
                             //`TagVector` or `TagSet` when using std::set,
                             //to match the terminology of C++

    TagIterable myFunction(const Tag& tag,const File& file);
                             //One may use references to const here,
                             //to explicitly avoid unnecessary copies

};

【讨论】:

    【解决方案2】:

    作为我的类型狂人,我总是包含新类型。

    编写包装代码很简单:

    class Tag {
    public:
        Tag() {}
        explicit Tag(std::string const& v): _value(v) {}
        std::string const& get() const { return _value; }
    private:
        std::string _value;
    }; // class Tag
    

    就是这样!

    typedef 很好,但是很容易混淆它们并使用没有意义的操作:Tag::substr 是什么意思?

    【讨论】:

      【解决方案3】:

      有一些包装(简单)类型的有效用例:

      • 使类型不同。例如对于某些上下文来说是唯一的标识符。
      • 给类型一个单位(美制加仑 3.79 与英制加仑 4.546) - 例如 boost::unit。 单位实际上是一个附加信息。
      • ...

      对于每个包装,您将创建一个不同的类(模板)以确保类型安全。 一个简单的 typedef 不会封装任何东西,只会澄清用法。

      【讨论】:

        【解决方案4】:

        我更愿意使用 c++ 为您提供的功能,除非 真的 需要创建别名。为什么不只使用自言自语的识别器?我的解决方案如下所示:

        std::vector<std::string> getTagsFromFile(std::string tag, std::string fileName);
        

        【讨论】:

        • 一般来说越简单越好。需要考虑的其他一点是,将其封装起来将使其在实施更改时不会过时。
        【解决方案5】:

        它称为encapsulation(也称为数据隐藏)。如果封装FileTag 的概念增加了价值,那么就去做吧,否则只会增加复杂性。可能是稍后您决定在类内部使用std::wstring 而不是简单的std::string,如果您创建TagFile 类,您可以轻松更改它,并且这些类的用户很可能不会受到影响。

        至于从 std::vector 继承,这确实是你应该避免的。但请记住:继承和封装是两个完全不同的概念。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多