C ++ - Äquivalent zu Javas toString?

151

Ich möchte steuern, was in einen Stream geschrieben wird, dh coutfür ein Objekt einer benutzerdefinierten Klasse. Ist das in C ++ möglich? In Java können Sie die toString()Methode für ähnliche Zwecke überschreiben .

Bogdan Balan
quelle

Antworten:

176

In C ++ können Sie operator<<für ostreamund Ihre benutzerdefinierte Klasse überladen :

class A {
public:
  int i;
};

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

Auf diese Weise können Sie Instanzen Ihrer Klasse in Streams ausgeben:

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

Wenn Sie operator<<Interna der Klasse ausdrucken Amöchten und wirklich Zugriff auf ihre privaten und geschützten Mitglieder benötigen, können Sie sie auch als Freundfunktion deklarieren:

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 << ")";
}
etw
quelle
16
Es ist besser, den Operator << als Freundfunktion der Klasse zu deklarieren, da er möglicherweise für den Zugriff auf die privaten Mitglieder der Klasse erforderlich ist.
Naveen
5
Besser noch deklarieren Sie es als friendund auch innerhalb des Hauptteils der Klasse - damit müssen Sie nicht using namespacefür den Namespace tun, der den Operator (und die Klasse) enthält, aber ADL wird es finden, solange das Objekt dieser Klasse ist einer der Operanden.
Pavel Minaev
... das obige sollte sagen " definiere es als Freund innerhalb des Körpers der Klasse" - wie in einer Inline-Member-Definition.
Pavel Minaev
2
@fnieto: Diese dumpöffentliche Methode ist schmutzig und unnötig. Die Verwendung friendhier ist vollkommen in Ordnung. Ob Sie eine redundante oder eine aufdringliche Methode bevorzugen, friendist völlig Geschmackssache, obwohl friendsie wohl genau für diesen Zweck eingeführt wurde.
Konrad Rudolph
1
@Pavel: Argumentabhängige Suche findet es trotzdem, solange der Operator im selben Namespace wie die Klasse definiert ist. Dies hat nichts mit Freunden zu tun und muss nicht innerhalb der Klasse deklariert / definiert werden. Außerdem operator<<()funktioniert das Erstellen einer Member-Funktion nicht: Sie müssten sie zu einer Member-Funktion machen, damit sie einen std::ostreamOperanden vom Typ für die linke Hand akzeptiert std::ostream.
etw
50

Sie können dies auch auf diese Weise tun und Polymorphismus zulassen:

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
quelle
3
+1 für die virtuelle Funktion, um das toStringVerhalten von Java zu kopieren .
Konrad Rudolph
Warum dumm, anstatt den Operator << in der Klasse direkt anzugeben?
Mönch
1
beacause Sie nicht wollen , eine Endlos - Schleife und einen Absturz haben
fnieto - Fernando Nieto
1
Vielleicht ist diese Technik schnell und einfach, um Optionen für die Serialisierung zu übergeben. Andernfalls müsste ein anderer klassenfreundlicher Operator << definiert werden, der mit den zu serialisierenden Optionen und Daten initialisiert wird.
Samuel Danielson
Ein weiterer Punkt wäre, dass die Implementierung der Dump-Funktionalität durch eine Schnittstelle erzwungen werden könnte, was mit dem vorgeschlagenen Operator nicht möglich wäre.
jupp0r
28

In C ++ 11 wird to_string schließlich zum Standard hinzugefügt.

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

Zhaojun Zhang
quelle
15
Dies ist eine nützliche Ergänzung zu dieser Seite, die C ++ - Implementierung unterscheidet sich jedoch erheblich von der in Java / C #. In diesen Sprachen ToString()ist eine virtuelle Funktion für die Basisklasse aller Objekte definiert und wird daher als Standardmethode zum Ausdrücken einer Zeichenfolgendarstellung eines beliebigen Objekts verwendet. Diese Funktionen std::stringgelten nur für integrierte Typen. Die idiomatische Methode in C ++ besteht darin, den <<Operator für benutzerdefinierte Typen zu überschreiben .
Drew Noakes
9
Die "Hässlichkeit" der Standardsignatur von operator<<im Vergleich zur einfachen StringSemantik von Java veranlasst mich zu der Bemerkung, dass dies to_string()nicht nur "eine nützliche Ergänzung" ist, sondern auch die neue bevorzugte Methode, dies in C ++ zu tun. Wenn wie beim OP eine benutzerdefinierte Zeichenfolgendarstellung einer Klasse Agewünscht wird, schreiben Sie einfach eine der string to_string(A a)folgenden Definitionen von class ASuffizienz. Dies wird mit Vererbung wie in Java weitergegeben und kann (durch Hinzufügen von Zeichenfolgen) wie in Java kombiniert werden. Das Nicht-Überschreiben toString()in Java ist ohnehin von begrenztem Nutzen.
P Marecki
10

Wenn Sie die Zeichenfolgendarstellung extrahieren und in einer folgenden std::stringDatei speichern möchten, gehen Sie wie folgt vor :

#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::stringstreambefindet sich in der <sstream>Kopfzeile.

blwy10
quelle
2
Das ist eine lächerlich umständliche Methode, um eine Serialisierungszeichenfolge zu erhalten!
Gerd Wagner
9

Die Frage wurde beantwortet. Aber ich wollte ein konkretes Beispiel hinzufügen.

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

Dieses Beispiel erfordert das Verständnis der Bedienerüberlastung.


quelle