3

以下是字符串类的抽象。

class string {
public:
    string(int n = 0) : buf(new char[n + 1]) { buf[0] = '\0'; }
    string(const char *);
    string(const string &);
    ~string() { delete [] buf; }

    char *getBuf() const;
    void setBuf(const char *);
    string & operator=(const string &);
    string operator+(const string &);
    string operator+(const char *);

    private:
       char *buf;
    };

    string operator+(const char *, const string &);
    std::ostream& operator<<(std::ostream&, const string&);

我想知道为什么这两个运算符重载函数

  string operator+(const char *, const string &);
  std::ostream& operator<<(std::ostream&, const string&);

不是类成员函数或友元函数吗?我知道两个参数运算符重载函数通常是友元函数(我不确定,如果您也能对此有所启发,我将不胜感激)但是我的教授也没有将它们声明为友元。以下是这些函数的定义。

 string operator+(const char* s, const string& rhs) {

           string temp(s);
           temp = temp + rhs;
           return temp;
 }

 std::ostream& operator<<(std::ostream& out, const string& s) {
     return out << s.getBuf();
 }

任何人都可以用一个小例子来解释这一点,或者指导我回答类似的问题。提前致谢。问候

4

4 回答 4

2

让我们谈谈运算符+。将其作为非成员允许以下代码

string s1 = "Hi";
string s2 = "There";
string s3;

s3 = s1 + s2;
s3 = s1 + "Hi";
s3 = "Hi" + s1;

如果 operator+ 是成员而不是命名空间范围函数,则最后一个赋值语句是不可能的。但如果是命名空间范围函数,则字符串文字“Hi”会使用转换构造函数“string(const char *);”转换为临时字符串对象。并传递给 operator+。

在您的情况下,可以在不将此功能设为朋友的情况下进行管理,因为您拥有私有成员“buf”的访问器。但通常,如果出于某种原因未提供此类访问器,则需要将这些命名空间范围函数声明为友元。

现在让我们谈谈运算符<<。

这是为 ostream 对象定义的插入运算符。如果他们必须打印用户定义类型的对象,则需要修改 ostream 类定义,不建议这样做。

因此,运算符在命名空间范围内被重载。

在这两种情况下,都有一个众所周知的Argument Dependent Lookup原理,这是查找这些命名空间范围函数(也称为 Koenig Lookup)背后的核心原因。

另一个有趣的读物是命名空间接口原则

于 2013-01-24T03:47:32.877 回答
2

friend关键字授予对 a 的protectedprivate成员的访问权限class。它没有在您的示例中使用,因为这些函数不需要使用 ; 的内部stringpublic接口就够了。

friend函数永远不是类的成员,即使在class {}作用域内定义也是如此。这是一个比较混乱的问题。有时friend用作在class {}大括号内定义非成员函数的技巧。但是在您的示例中,没有什么特别的,只有两个功能。这些函数恰好是运算符重载。

将一些重载定义为成员,将一个重载定义为非成员,这是一种糟糕的风格。operator+通过使所有这些都成为非成员来改进界面。this不同的类型转换规则应用于成为重载函数内部的左侧参数,这可能会导致令人困惑的错误。所以交换运算符通常应该是非成员(friend或不是)。

于 2013-01-24T03:54:07.270 回答
1

运算符可以被成员函数和独立(普通)函数重载。独立重载函数是否是朋友完全无关紧要。友谊属性与运算符重载绝对没有关系。

当您使用独立函数时,您可能需要直接访问类的“隐藏”(私有或受保护)内部,即当您将函数声明为friend. 如果您不需要这种特权访问(即您可以根据类的公共接口实现所需的功能),则无需将函数声明为friend.

这里的所有都是它的。

将独立的重载函数声明为朋友变得如此流行,以至于人们经常将其称为“由朋友函数重载”。这确实是一个误导性的误称,因为正如我上面所说,友谊本身与它无关。

此外,人们有时会声明重载函数,friend即使他们不需要对类的特权访问。他们这样做是因为friend函数声明可以在类定义中包含函数的直接内联定义。没有friend人将被迫做一个单独的声明和一个单独的定义。在某些情况下,紧凑的内联定义可能看起来“更干净”。

于 2013-01-24T04:04:12.037 回答
0

我对 C++ 重载有点生疏,但我会通过这个简单的备忘录来完成上述答案:

  • 如果左侧操作数的类型是用户定义的类型(例如类),您应该(但您不必)将运算符重载实现为成员函数。请记住,如果这些重载——很可能像++=++ ...—— 修改左侧操作数,它们会返回调用类型的引用(实际上是修改后的对象) . 这就是为什么,例如在 Coplien 的规范形式中,operator=重载是一个成员函数并返回一个“UserClassType &”(因为实际上该函数返回*this)。

  • 如果左侧操作数的类型是系统类型intostream等...),则应将运算符重载实现为独立函数。

顺便说一句,我一直被告知friend关键字是坏的、丑陋的和吃孩子的。我想这主要是编码风格的问题,但我建议你在使用它时要小心,并尽可能避免它。(我从来没有遇到过强制使用它的情况,所以我真的说不出来!)

(对不起我的英语不好,我也有点生疏)

于 2013-01-24T14:52:38.990 回答