【问题标题】:Overloading operator<< for printing as a member重载 operator<< 以作为成员打印
【发布时间】:2012-01-18 13:01:42
【问题描述】:

有没有办法重载

class TestClass {
public:
    ostream& operator<<(ostream& os) {
        return os << "I'm in the class, msg=" << msg << endl;
    }

private:
    string msg;
};


int main(int argc, char** argv) {
    TestClass obj = TestClass();
    cout << obj;

    return 0;
}

我能想到的唯一方法是在类之外重载运算符:

ostream& operator<<(ostream& os, TestClass& obj) {
    return os << "I'm outside of the class and can't access msg" << endl;
}

但是访问对象的私有部分的唯一方法是与 operator 函数为朋友,如果可能的话,我宁愿避免朋友,因此请您提供替代解决方案。

任何有关如何进行的 cmets 或建议都会有所帮助:)

【问题讨论】:

标签: c++ operator-overloading iostream


【解决方案1】:

它必须是非成员,因为该类构成运算符的第二个参数,而不是第一个。如果仅使用公共接口即可完成输出,那么您就完成了。如果它需要访问非公共成员,那么您必须将其声明为朋友;这就是朋友的目的。

class TestClass {
public:
    friend ostream& operator<<(ostream& os, TestClass const & tc) {
        return os << "I'm a friend of the class, msg=" << tc.msg << endl;
    }

private:
    string msg;
};

【讨论】:

【解决方案2】:

您偶然发现了实现此功能的规范方法。你所拥有的是正确的。

【讨论】:

  • 这是不正确的,因为它不会编译。在问题的设置中,必须写一些丑陋的东西,比如obj&lt;&lt;cout;
【解决方案3】:

我相信一种流行的方法是非会员、非朋友免费operator&lt;&lt; 在您的班级中调用公共非虚拟print 方法。此打印方法既可以完成工作,也可以委托给受保护的虚拟实现。

class TestClass {
public:
    ostream& print(ostream& os) const {
        return os << "I'm in the class, msg=" << msg << endl;
    }

private:
    string msg;
};


ostream& operator<<(ostream& os, TestClass& obj) {
    return obj.print(os);
}

int main(int argc, char** argv) {
    TestClass obj;
    cout << obj;

    return 0;
}

【讨论】:

  • 我使用朋友声明支持上述解决方案。它维护封装并完成工作。
  • @broc 我看不出封装有任何真正的区别。在一种情况下,该类的作者明确授予operator&lt;&lt;() 的单个实例的访问权限;在这个方法中,他明确地授予了一个名为print() 的函数的访问权限。区别在哪里? (使用print() 方法可能还有其他原因,但或多或​​少的封装不是其中之一。)
  • @JamesKanze 您可能会争辩说,宣布成员为公众只是作者明确授予每个人访问权限,因此它不会破坏封装。你买不买取决于你在哪里画线。我个人认为,线一定要尽量往反方向画;拥有 print 方法允许您在 .dll 中分发类,在没有源的情况下分发它,并且仍然允许库的用户编写他们的重载 operator
  • @JamesKanze 只是语言我不能不同意你,但是,该语言用于创建可执行文件或库,我认为出于专业开发的目的,封装的概念必须超越仅仅根据语言进行思考。
  • @broc 声明一个成员 public 会放松封装,因为它使成员成为公共接口的一部分。声明一个函数friend 使该函数成为类的公共接口的一部分;通过使函数成为接口的一部分,而不是数据,您实际上是在增加封装。太多的函数(或设计不佳的函数,如 getter 和 setter)也会削弱封装性,但在这方面,函数是朋友还是成员无关紧要。
【解决方案4】:

您可以使其成为该类的成员,即&lt;&lt; 的左侧,在您的情况下为ostream

但是,您可以做的是为您的所有流媒体和非成员 operator&lt;&lt; 创建一个基类,其中包含 void do_stream(ostream&amp; o); 成员。

【讨论】:

  • 我没有。我只是在说明这样一个事实,如果希望它成为班级成员,班级应该是std::ostream。这基本上意味着不,您不希望它成为班级成员。
【解决方案5】:

你是对的,这是实现流操作符的唯一方法 - 在类之外。

您需要将方法声明为friend

就是这样。

【讨论】:

  • 通常不需要将函数声明为friend;您输出的任何内容都是该类的公共接口的一部分。 (为了使用 Barton 和 Nackman 技巧,在模板中将其声明为朋友通常是合适的,但这是一个不同的问题。)
  • 这真的不是实现它的唯一方法。我已经看到并实现了大多数流运算符,但没有将它们绑定为friend。只是必要的,应该使用friend
  • @phresnel 我倾向于经常使用print 方法和类模板。不是为了获得访问权限,而是为了能够在类中定义函数。
  • 在他的情况下,成员是私人的,所以需要友谊。这比添加 getter 只供流使用要简单得多。
【解决方案6】:

你必须使它成为非成员(因为第一个参数不是你的类)。

但是您可以将其写在您的类定义中(作为朋友):

class TestClass
{
public:
    // Have a nice friend.
    // This tightly binds this operator to the class.
    // But that is not a problem as in reality it is already tightly bound.
    friend ostream& operator<<(ostream& os, TestClass const& data)
    {
        return os << "I'm in the class, msg=" << data.msg << endl;
    }

private:
    string msg;
};

我觉得交这个朋友没什么错。

【讨论】:

    猜你喜欢
    • 2011-07-19
    • 2019-02-24
    • 2012-04-06
    • 2015-03-08
    • 2022-01-07
    • 1970-01-01
    • 2019-06-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多