C++中等价于Java的toString方法?

178

我想控制写入流的内容,比如一个自定义类对象的cout。在C++中是否可能实现?在Java中,你可以通过重写toString()方法来实现类似的功能。

5个回答

197

在 C++ 中,您可以为 ostream 和自定义类重载 operator<<

class A {
public:
  int i;
};

std::ostream& operator<<(std::ostream &strm, const A &a) {
  return strm << "A(" << a.i << ")";
}

这种方式可以在流中输出类的实例:

A x = ...;
std::cout << x << std::endl;

如果你的 operator<< 想要打印类 A 的内部并且确实需要访问它的私有和受保护成员,你也可以将其声明为友元函数:

class A {
private:
  friend std::ostream& operator<<(std::ostream&, const A&);
  int j;
};

std::ostream& operator<<(std::ostream &strm, const A &a) {
  return strm << "A(" << a.j << ")";
}

5
更好的做法是将其声明为friend,并在类的内部进行声明 - 这样一来,您就不必为包含运算符(和类)的命名空间使用using namespace,但只要该类的对象是操作数之一,ADL 就会找到它。 - Pavel Minaev
上面的意思是在类的内部“定义”它为友元——也就是说,一个内联成员定义。 - Pavel Minaev
2
@fnieto:那个dump公共方法很不好,也没必要。在这里使用friend是完全可以的。你喜欢多余的方法还是侵入式的friend完全是个人口味问题,尽管可以说friend就是为了这个目的而引入的。 - Konrad Rudolph
1
@Pavel:只要在与类相同的命名空间中定义操作符,参数依赖查找仍然会找到它。这与友元没有任何关系,也不需要在类内声明/定义。此外,将operator<<()作为成员函数无法起作用:您必须将其作为std::ostream的成员函数,以使其接受类型为std :: ostream的左操作数。 - sth

57

你还可以使用这种方法,实现多态:

class Base {
public:
   virtual std::ostream& dump(std::ostream& o) const {
      return o << "Base: " << b << "; ";
   }
private:
  int b;
};

class Derived : public Base {
public:
   virtual std::ostream& dump(std::ostream& o) const {
      return o << "Derived: " << d << "; ";
   }
private:
   int d;
}

std::ostream& operator<<(std::ostream& o, const Base& b) { return b.dump(o); }

4
为了复制Java中 toString 的行为,建议使用虚函数,并给其加上 +1。 - Konrad Rudolph
1
因为您不希望出现无限循环和崩溃。 - Fernando N.
1
也许这种技术在传递序列化选项方面快速简便。否则,将需要定义另一个类来初始化带有选项和要序列化的数据的operator<< 。 - Samuel Danielson
另一个要点是,通过接口可以强制实施转储功能的实现,而使用建议的运算符则不可能。 - jupp0r
不知道怎么样才能像这样添加“const”:virtual std::ostream& dump(std::ostream& o) const { ... } - Qi Fan
显示剩余4条评论

33

17
这是对该页面的有用补充,但C++实现与Java/C#中的实现有显著不同。在那些语言中,“ToString()”是定义在所有对象的基类上的虚函数,因此被用作表达任何对象的字符串表示的标准方式。而这些针对“std::string”的函数仅适用于内置类型。在C++中惯用的方式是为自定义类型重载“<<”运算符。 - Drew Noakes
12
与Java中简单的String语义相比,operator<<标准签名的“丑陋”促使我评论说,to_string()不仅是“有用的补充”,而且是在C++中进行此操作的新首选方法。如果需要类A的自定义字符串表示,则在class A的定义下方编写string to_string(A a)就足够了。这将像Java一样传播到继承中,并且可以通过字符串添加(如Java)组合。在Java中,未重写的toString()本来就有限制使用。 - P Marecki

12
该问题已得到解答。但是我想添加一个具体的例子。
class Point{

public:
      Point(int theX, int theY) :x(theX), y(theY)
      {}
      // Print the object
      friend ostream& operator <<(ostream& outputStream, const Point& p);
private:
      int x;
      int y;
};

ostream& operator <<(ostream& outputStream, const Point& p){
       int posX = p.x;
       int posY = p.y;

       outputStream << "x="<<posX<<","<<"y="<<posY;
      return outputStream;
}

这个例子需要理解运算符重载。


12

扩展John所说的内容,如果你想要提取字符串表达式并将其存储在std::string中,请使用以下代码:

#include <sstream>    
// ...
// Suppose a class A
A a;
std::stringstream sstream;
sstream << a;
std::string s = sstream.str(); // or you could use sstream >> s but that would skip out whitespace

std::stringstream位于<sstream>头文件中。


3
那是一种荒谬繁琐的获取序列化字符串的方式! - Gerd Wagner
你可以将它作为一个类外的模板函数,接受一个重载了operator<<的T类型的引用,并返回一个字符串。 - user904542
你可以将它作为一个类外的模板函数,接受一个重载了operator<<的T类型的引用,并返回一个字符串。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接