¿C ++ equivalente de toString de Java?

151

Me gustaría controlar lo que está escrito en una secuencia, es decir cout, para un objeto de una clase personalizada. ¿Es eso posible en C ++? En Java, podría anular el toString()método para un propósito similar.

Bogdan Balan
fuente

Respuestas:

176

En C ++ se puede sobrecargar operator<<de ostreamy la clase personalizada:

class A {
public:
  int i;
};

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

De esta manera, puede generar instancias de su clase en secuencias:

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

En caso de que operator<<desee imprimir elementos internos de la clase Ay realmente necesite acceso a sus miembros privados y protegidos, también puede declararlo como una función amiga:

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 << ")";
}
algo
fuente
16
Es mejor declarar al operador << como función amiga de la clase, ya que podría ser necesario para acceder a los miembros privados de la clase.
Naveen
55
Mejor aún declararlo como friend, y también dentro del cuerpo de la clase; con eso, no tendrá que hacer using namespacepara el espacio de nombres que contiene el operador (y la clase), pero ADL lo encontrará siempre que el objeto de esa clase sea Uno de los operandos.
Pavel Minaev
... lo anterior estaba destinado a decir " definirlo como amigo dentro del cuerpo de la clase", como en una definición de miembro en línea.
Pavel Minaev
2
@fnieto: ese dumpmétodo público es sucio e innecesario. Usar friendaquí está perfectamente bien. Si prefiere un método redundante o intrusivo friendes una cuestión de gustos, aunque friendpodría decirse que se ha introducido para este propósito exacto.
Konrad Rudolph el
1
@Pavel: la búsqueda dependiente del argumento lo encontrará de todos modos, siempre que el operador esté definido en el mismo espacio de nombres que la clase. Esto no tiene nada que ver con amigos y no necesita ser declarado / definido dentro de la clase. Además, hacer operator<<()una función miembro no funcionará: tendría que hacer que sea una función miembro std::ostreampara que acepte un operando de tipo izquierdo std::ostream.
sth
50

También puede hacerlo de esta manera, permitiendo el polimorfismo:

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); }
fnieto - Fernando Nieto
fuente
3
+1 para la función virtual, para copiar el toStringcomportamiento de Java .
Konrad Rudolph el
¿Por qué tonto en lugar de especificar directamente el operador << en la clase?
monksy
1
porque no quieres tener un bucle infinito y un accidente
fnieto - Fernando Nieto
1
Quizás esta técnica es rápida y fácil para pasar opciones sobre qué serializar. De lo contrario, sería necesario definir otro operador de amistad de clase << que se inicializa con las opciones y los datos para serializar.
Samuel Danielson
Otro punto sería que la implementación de la funcionalidad de volcado podría aplicarse mediante una interfaz, que no sería posible utilizando el operador propuesto.
jupp0r
29

En C ++ 11, to_string finalmente se agrega al estándar.

http://en.cppreference.com/w/cpp/string/basic_string/to_string

Zhaojun Zhang
fuente
15
Esta es una adición útil a esta página, sin embargo, la implementación de C ++ es significativamente diferente a la de Java / C #. En esos idiomas, ToString()es una función virtual definida en la clase base de todos los objetos y, por lo tanto, se utiliza como una forma estándar para expresar una representación de cadena de cualquier objeto. Estas funciones std::stringsolo se aplican a los tipos integrados. La forma idiomática en C ++ es anular el <<operador para los tipos personalizados.
Drew Noakes
9
La "fealdad" de la firma estándar de operator<<, en comparación con la Stringsemántica simple de Java, me lleva a comentar que to_string()no solo es "una adición útil", sino la nueva forma preferida de hacerlo en C ++. Si, como por el OP, Ase desea una representación de cadena personalizada de una clase , simplemente escribiendo una string to_string(A a)definición de class Asufijos a continuación. Esto se propaga con herencia como en Java, y se puede combinar (mediante la adición de cadenas) como en Java. No anulado toString()en Java es de uso limitado de todos modos.
P Marecki
10

Como una extensión de lo que dijo John, si desea extraer la representación de cadena y almacenarla, std::stringhaga lo siguiente:

#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::stringstreamse encuentra en el <sstream>encabezado.

blwy10
fuente
2
¡Esa es una forma ridícula y engorrosa de obtener una cadena de serialización!
Gerd Wagner
9

La pregunta ha sido respondida. Pero quería agregar un ejemplo concreto.

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

Este ejemplo requiere comprender la sobrecarga del operador.


fuente