自从我上次使用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 ++哲学建议做什么?
答案 0 :(得分:5)
在这种情况下我经常使用typedef
:
typedef
没有引入std::vector<>
本身的新类型或讨厌的衍生物,但对于使用您的代码的人来说,别名可能仍然是令人讨厌的间接。std::string
功能)。typedef
更改为具有额外属性和功能的新类别,或仅用于类型安全。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(也称为数据隐藏)。如果强加File
和Tag
的概念会增加价值,那么请继续执行,否则您只会增加复杂性。可能以后您决定在类中使用std::wstring
而不是内部的简单std::string
,如果您创建Tag
和File
类,则可以轻松更改,并且这些类的用户很可能不会受到影响。
至于从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)
包装(简单)类型有一些有效的用例:
对于每个包装,您将创建一个不同的类(模板)以确保类型安全。 一个简单的typedef不会封装任何东西,只能澄清用法。