<强>背景

我有一个容器类,它使用vector&lt; std :: string&gt;内部。我已经为这个包装类提供了一个方法 AddChar(std :: string),它将 push_back()传递给内部向量。在我的代码中,我必须在容器中添加多个项目。为此,我必须使用

container.AddChar("First");
container.AddChar("Second");

这使代码更大。因此,为了使它更容易,我计划重载运算符&lt;&lt;。这样我就可以写了

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;
    }
};

它按预期工作。

<强>问题

  1. 是否正确写入了运算符重载?
  2. 在这样的情况下重载运营商是一种好习惯吗?
  3. 此代码是否存在任何性能问题或任何其他问题?
  4. 有什么想法吗?

    修改

    在听到优秀评论后,我决定不重载&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")
    

    感谢您的回复。

有帮助吗?

解决方案

  

是否正确写入了运算符重载?

确实如此,但人们可以做得更好。与其他人提到的一样,您的功能可以完全由现有的公共功能定义。为什么不让它只使用那些?现在,它是一个朋友,这意味着它属于实现细节。如果你把运算符&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预处理器库此处

下一个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 ++中,也可以使用可变参数模板。 GCC4.4将为他们提供支持。 GCC 4.3已经部分支持它们。

其他提示

1)是的,除非因为AddChar是公开的,所以没有必要成为朋友

2)这是有争议的。 &lt;&lt;&lt; 处于作为运算符的位置,其“重复”为“奇怪”的重载。事情至少是勉强接受的。

3)没有什么明显的。一如既往,剖析是你的朋友。您可能需要考虑通过const引用( const std :: string&amp; )将字符串参数传递给 AddChar operator&lt;&lt; 以避免不必要的复制。

  

过载是一种好习惯   在这样的情况下运营商?

我不这么认为。对于那些不知道你已经超载运营商的人来说,这让人感到困惑。只需坚持描述性的方法名称,忘记你正在输入的额外字符,它就是不值得的。你的维护者(或你自己在6个月内)会感谢你。

我不希望以个人方式重载它,因为向量通常不会有一个超载的左移运算符 - 它不是真正的成语; - )

我可能会从AddChar返回一个引用,如下所示:

ExtendedVector& AddChar(const std::string& str) {
    container.push_back(str);
    return *this;
}

所以你可以这样做

container.AddChar("First").AddChar("Second");

并不比bitshift运算符大得多。

(另请参阅Logan关于通过引用而不是按值传递字符串的注释)。

在这种情况下,运算符重载并不是一种好习惯,因为它会降低代码的可读性。标准 std :: vector 没有用于推送元素,原因很充分。

如果你担心调用者代码太长,你可以考虑这个而不是重载的运算符:

container.AddChar("First").AddChar("Second");

如果您有 AddChar()返回 * this ,这将是可能的。

你有这个 toString()函数很有趣。在那个的情况下,输出到流的运算符&lt;&lt; 将是标准的使用方式!因此,如果您想使用运算符,请将 toString()函数设为运算符&lt;&lt;

此处操作员未正确重载。没有理由让操作员成为朋友,因为它可以是班级的成员。 Friend用于不是类的实际成员的函数(例如当重载&lt;&lt;对于ostream时,对象可以输出到cout或ofstreams)。

您真正希望操作员:

ExtendedVector& operator<<(const std::string str){
    AddChar(str);
    return *this;
}

通常认为以一种比正常情况更多的方式重载运算符是不好的做法。 &LT;&LT;通常是位移,因此以这种方式重载可能会令人困惑。显然STL重载&lt;&lt;用于“流插入”的所以可能有意义地重载它以便以类似的方式使用它。但这似乎不像你正在做的那样,所以你可能想避免它。

没有性能问题,因为运算符重载与常规函数调用相同,只是调用被隐藏,因为它是由编译器自动完成的。

这会让事情变得相当混乱,我会使用与std :: cin相同的语法到变量中:

std :: cin&gt;&gt; someint;

<代码>&QUOT;首先&QUOT; &GT;&GT;容器;

这样它至少是一个插入操作符。当任何东西都有&lt;&lt;重载运算符我希望它输出一些东西。就像std :: cout。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top