c++ wrapping types for semantic
自从我上次使用C++,从Java和Python回来以来,已经有很长时间了,我有一个关于C++的好实践的问题:
我想保留一些非常简单的对象的语义代码,比如说,我们有对象标记和文件,它们都只是std::string和一个类标记管理器,其中包含使用标记和文件的多个函数。
我的问题是,创建一个表示这些琐碎对象的自定义类型还是直接将它们用于它们是什么?
更具体地说,我可以为函数定义如下:
1 2 3 | 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); |
我更喜欢第一种解决方案,因为我喜欢保留语义代码,而另一方面,这样做需要用户检查这些类型是什么,因此它减少了使用的简单性。
我还读到了另一个问题(你不能从std::vector继承)"不要仅仅为了让事情看起来更好而产生新的实体。"
那么在这种情况下你会做什么?C++哲学建议做什么?
在这种情况下,我通常使用
- 虽然
typedef 没有引入新的类型或std::vector<> 本身的讨厌的派生,但是别名可能仍然会对那些使用代码的人造成干扰。 - 另一方面,它们可以被很好地公开和记录(提供一个例子),这样每个人都可以从一开始就使用它们。
- 它们有助于避免构建断路器,以防需要更改类型(前提是,在您的特定情况下,没有使用或在替换中提供特殊的
std::string 功能)。 - 因此,如果需要,可以稍后将
typedef 更改为使用额外属性和函数的新类别名,或者只是为了类型安全。 - 它们允许轻松跟踪使用位置,以及在大多数IDE中重构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 }; |
它被称为封装(也称为数据隐藏)。如果将
至于从std::vector继承,这确实是您应该避免的。但请记住:继承和封装是两个完全不同的概念。
作为一个类型怪胎,我是,我总是包括新类型。
编写包装器代码非常简单:
1 2 3 4 5 6 7 8 | 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 |
就这样!
我宁愿使用C++所提供的,除非它真的需要制造别名。为什么不只用自言自语的身份证呢?我的解决方案如下:
1 | std::vector<std::string> getTagsFromFile(std::string tag, std::string fileName); |
包装(简单)类型有一些有效的用例:
- 使类型与众不同。例如,对于某些上下文来说是唯一的标识符。
- 给出A型单位(美国加仑3.79 vs.帝国加仑4.546)-例如Boost::Unit。这个单元实际上是一个附加信息。
- …
对于每个包装,您将创建一个不同的类(模板),以确保类型安全。一个简单的typedef不会封装任何东西,只会澄清用法。