背景
我有一个容器类,它使用vector< std :: string>内部。我已经为这个包装类提供了一个方法 AddChar(std :: string),它将 push_back()传递给内部向量。在我的代码中,我必须在容器中添加多个项目。为此,我必须使用
container.AddChar("First");
container.AddChar("Second");
这使代码更大。因此,为了使它更容易,我计划重载运算符<<。这样我就可以写了
container << "First" << "Second"
并将两个项目添加到基础矢量。
以下是我用于
的代码class ExtendedVector
{
private:
vector<string> container;
public:
friend ExtendedVector& operator<<(ExtendedVector& cont,const std::string str){
cont.AddChar(str);
return cont;
}
void AddChar(const std::string str)
{
container.push_back(str);
}
string ToString()
{
string output;
vector<string>::iterator it = container.begin();
while(it != container.end())
{
output += *it;
++it;
}
return output;
}
};
它按预期工作。
问题
有什么想法吗?
修改
在听到优秀评论后,我决定不重载&lt;&lt;因为这里没有意义。我删除了操作符重载代码,这是最终代码。
class ExtendedVector
{
private:
vector<string> container;
public:
ExtendedVector& AddChar(const std::string str)
{
container.push_back(str);
return *this;
}
.. other methods
}
这允许我添加
container.AddChar("First").AddChar("Second")
在C#中,我可以使用params关键字更轻松地完成此操作。代码就像
void AddChar(params string[] str)
{
foreach(string s in str)
// add to the underlying collection
}
我知道在C ++中,我们可以使用 ... 来指定参数的变量长度。但是AFAIK,它不是类型安全的。这样做是推荐的做法吗?这样我就可以写了
container.AddChar("First","Second")
感谢您的回复。
答案 0 :(得分:8)
是否正确写入了运算符重载?
确实如此,但人们可以做得更好。与其他人提到的一样,您的功能可以完全由现有的公共功能定义。为什么不让它只使用那些?现在,它是一个朋友,这意味着它属于实现细节。如果你把运算符&lt;&lt;&lt;&lt;&lt;&lt;作为你班上的一员。但是,请使您的运营商&lt;&lt; 非会员,非朋友功能。
class ExtendedVector {
...
};
// note, now it is *entirely decoupled* from any private members!
ExtendedVector& operator<<(ExtendedVector& cont, const std::string& str){
cont.AddChar(str);
return cont;
}
如果您更改了课程,则无法确定您的操作员&lt;&lt;仍然有效。但是,如果您的运营商&lt;&lt;完全取决于公共函数,那么只有在对类的实现细节进行更改后,才能确保它能够正常工作。耶!
在这种情况下重载运算符是一种好习惯吗?
另一个人再说一遍,这是有争议的。在许多情况下,操作员超载一见钟情看起来很“整洁”,但明年看起来会像地狱一样,因为在给予一些符号特别的爱时,你不再知道你的想法。在运营商&lt;&lt;的情况下,我认为这是一个好的用途。它用作流的插入操作符是众所周知的。我知道在像
这样的情况下广泛使用它的Qt和KDE应用程序QStringList items;
items << "item1" << "item2";
类似的情况是boost.format
,它还重用operator%
来为其字符串中的占位符传递参数:
format("hello %1%, i'm %2% y'old") % "benny" % 21
在那里使用它当然也是有争议的。但它用于printf格式指定是众所周知的,所以它的使用也是可以的,imho。但是和往常一样,风格也是主观的,所以要带上一粒盐:)
我如何以类型安全的方式接受变长参数?
嗯,如果你正在寻找同质论证,有接受矢量的方法:
void AddChars(std::vector<std::string> const& v) {
std::vector<std::string>::const_iterator cit =
v.begin();
for(;cit != v.begin(); ++cit) {
AddChar(*cit);
}
}
传递它并不是很舒服。你必须手动构建你的矢量然后传递...我看到你已经对vararg风格函数有了正确的感觉。不应该将它们用于这种代码,只有在与C代码接口或调试函数时才使用它们。处理这种情况的另一种方法是应用预处理器编程。这是一个高级主题,非常黑客。我们的想法是自动生成一个大小超过某个上限的重载:
#define GEN_OVERLOAD(X) \
void AddChars(GEN_ARGS(X, std::string arg)) { \
/* now access arg0 ... arg(X-1) */ \
/* AddChar(arg0); ... AddChar(arg(N-1)); */ \
GEN_PRINT_ARG1(X, AddChar, arg) \
}
/* call macro with 0, 1, ..., 9 as argument
GEN_PRINT(10, GEN_OVERLOAD)
那是伪代码。您可以查看boost预处理器库here。
下一个C ++版本将提供更好的可能性。可以使用初始化列表:
void AddChars(initializer_list<std::string> ilist) {
// range based for loop
for(std::string const& s : ilist) {
AddChar(s);
}
}
...
AddChars({"hello", "you", "this is fun"});
在下一个C ++中,也可以使用variadic templates支持任意多个(混合类型)参数。 GCC4.4将为他们提供支持。 GCC 4.3已经部分支持它们。
答案 1 :(得分:3)
过载是一种好习惯 在这样的情况下运营商?
我不这么认为。对于那些不知道你已经超载运营商的人来说,这让人感到困惑。只需坚持描述性的方法名称,忘记你正在输入的额外字符,它就是不值得的。你的维护者(或你自己在6个月内)会感谢你。
答案 2 :(得分:3)
1)是的,但由于AddChar是公开的,所以没有必要成为friend
。
2)这是有争议的。 <<
有点像运营商一样,对于“怪异”事物的超载至少是勉强接受的。
3)没有什么明显的。一如既往,剖析是你的朋友。您可能需要考虑通过const引用(AddChar
)将字符串参数传递给operator<<
和const std::string&
,以避免不必要的复制。
答案 3 :(得分:2)
我不希望以个人方式重载它,因为向量通常不会有一个超载的左移运算符 - 它不是真正的成语; - )
我可能会从AddChar返回一个引用,如下所示:
ExtendedVector& AddChar(const std::string& str) {
container.push_back(str);
return *this;
}
所以你可以这样做
container.AddChar("First").AddChar("Second");
并不比bitshift运算符大得多。
(另请参阅Logan关于通过引用而不是按值传递字符串的注释)。
答案 4 :(得分:2)
在这种情况下,运算符重载并不是一种好习惯,因为它会降低代码的可读性。标准std::vector
没有用于推送元素,原因很充分。
如果你担心调用者代码太长,你可以考虑这个而不是重载的运算符:
container.AddChar("First").AddChar("Second");
如果您AddChar()
返回*this
,则可以使用此功能。
你有这个toString()
功能很有趣。在那个的情况下,输出到流的operator<<
将是标准的使用方式!因此,如果您想使用运算符,请将toString()
函数设为operator<<
。
答案 5 :(得分:1)
此处操作员未正确重载。没有理由让操作员成为朋友,因为它可以是班级的成员。 Friend用于不是类的实际成员的函数(例如当重载&lt;&lt;对于ostream时,对象可以输出到cout或ofstreams)。
您真正希望操作员:
ExtendedVector& operator<<(const std::string str){
AddChar(str);
return *this;
}
通常认为以一种比正常情况更多的方式重载运算符是不好的做法。 &LT;&LT;通常是位移,因此以这种方式重载可能会令人困惑。显然STL重载&lt;&lt;对于“流插入”等等,可能有意义地重载它以便以类似的方式使用它。但这似乎不像你正在做的那样,所以你可能想避免它。
没有性能问题,因为运算符重载与常规函数调用相同,只是调用被隐藏,因为它是由编译器自动完成的。
答案 6 :(得分:0)
这会让事情变得相当混乱,我会使用与std :: cin相同的语法到变量中:
std::cin >> someint;
"First" >> container;
这样它至少是一个插入操作符。当任何东西都有&lt;&lt;重载运算符我希望它输出一些东西。就像std :: cout。