c ++包装语义的类型

时间:2013-11-24 11:56:30

标签: c++

自从我上次使用c ++以来,从java和python回来已经很长时间了,我对c ++的良好实践提出了疑问:

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

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

更具体地说,我可以为函数定义其中一个定义:

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 ++哲学建议做什么?

5 个答案:

答案 0 :(得分:5)

在这种情况下我经常使用typedef

  • 即使typedef没有引入std::vector<>本身的新类型或讨厌的衍生物,但对于使用您的代码的人来说,别名可能仍然是令人讨厌的间接。
  • 另一方面,它们可以被曝光并记录良好(提供一个例子),以便每个人从一开始就使用它们。
  • 它们有助于避免构建断路器,以防需要更改类型(前提是,在您的特定情况下,未使用或在替换中提供特殊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

};

答案 1 :(得分:2)

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

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

答案 2 :(得分:1)

我更喜欢使用c ++提供的功能,除非确实需要制作别名。为什么不只使用自言自语的标识符?我的解决方案如下:

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

答案 3 :(得分:1)

作为我的类型怪物,我总是包含新类型。

编写包装器代码非常简单:

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意味着什么?

答案 4 :(得分:0)

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

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

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