Equivalent C ++ du toString de Java?

151

Je voudrais contrôler ce qui est écrit dans un flux, c'est cout-à- dire pour un objet d'une classe personnalisée. Est-ce possible en C ++? En Java, vous pouvez remplacer la toString()méthode à des fins similaires.

Bogdan Balan
la source

Réponses:

176

En C ++, vous pouvez surcharger operator<<pour ostreamet votre classe personnalisée:

class A {
public:
  int i;
};

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

De cette façon, vous pouvez générer des instances de votre classe sur des flux:

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

Si vous operator<<souhaitez imprimer les éléments internes de la classe Aet que vous avez vraiment besoin d'accéder à ses membres privés et protégés, vous pouvez également le déclarer comme une fonction d'ami:

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 << ")";
}
qc
la source
16
Il est préférable de déclarer l'opérateur << comme fonction amie de la classe car il peut être nécessaire pour accéder aux membres privés de la classe.
Naveen
5
Mieux encore le déclarer comme friend, et aussi à l'intérieur du corps de la classe - avec cela, vous n'aurez pas à faire using namespacepour l'espace de noms contenant l'opérateur (et la classe), mais ADL le trouvera tant que l'objet de cette classe est l'un des opérandes.
Pavel Minaev
... ce qui précède était censé dire " définissez- le comme ami à l'intérieur du corps de la classe" - comme dans, une définition de membre en ligne.
Pavel Minaev
2
@fnieto: cette dumpméthode publique est sale et inutile. Utiliser friendici est parfaitement bien. Que vous préfériez une méthode redondante ou une méthode intrusive friendest entièrement une question de goût, bien qu'elle friendait sans doute été introduite dans ce but précis.
Konrad Rudolph
1
@Pavel: La recherche dépendante de l'argument le trouvera de toute façon, tant que l'opérateur est défini dans le même espace de noms que la classe. Cela n'a rien à voir avec des amis et n'a pas besoin d'être déclaré / défini dans la classe. De plus, créer operator<<()une fonction membre ne fonctionnera pas: vous devrez en faire une fonction membre de std::ostreampour qu'elle accepte un opérande de gauche de type std::ostream.
STH
50

Vous pouvez également le faire de cette façon, en permettant le polymorphisme:

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
la source
3
+1 pour la fonction virtuelle, pour copier le toStringcomportement de Java .
Konrad Rudolph
Pourquoi stupide plutôt que de spécifier directement l'opérateur << dans la classe?
monksy
1
parce que vous ne voulez pas avoir une boucle infinie et un crash
fnieto - Fernando Nieto
1
Peut-être que cette technique est rapide et facile pour passer des options sur ce qu'il faut sérialiser. Sinon, il serait nécessaire de définir un autre opérateur de classe friending << qui est initialisé avec les options et les données à sérialiser.
Samuel Danielson
Un autre point serait que la mise en œuvre de la fonctionnalité de vidage pourrait être imposée par une interface, ce qui ne serait pas possible en utilisant l'opérateur proposé.
jupp0r
29

En C ++ 11, to_string est finalement ajouté au standard.

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

Zhaojun Zhang
la source
15
Il s'agit d'un ajout utile à cette page, mais l'implémentation C ++ est très différente de celle de Java / C #. Dans ces langages, ToString()est une fonction virtuelle définie sur la classe de base de tous les objets, et est donc utilisée comme moyen standard pour exprimer une représentation sous forme de chaîne de n'importe quel objet. Ces fonctions std::stringne s'appliquent qu'aux types intégrés. La manière idiomatique en C ++ est de remplacer l' <<opérateur pour les types personnalisés.
Drew Noakes
9
La "laideur" de la signature standard de operator<<, par rapport à la Stringsémantique simple de Java, m'incite à remarquer que ce to_string()n'est pas seulement "un ajout utile", mais la nouvelle façon préférée de le faire en C ++. Si, comme par l'OP, une représentation sous forme de chaîne personnalisée d'une classe Aest souhaitée, il suffit d'écrire une string to_string(A a)définition ci - dessous de class Asuffices. Cela se propage avec héritage comme en Java, et peut être combiné (par ajout de chaîne) comme en Java. Le non-overriden toString()en Java est de toute façon d'un usage limité.
P Marecki
10

En tant qu'extension de ce que John a dit, si vous souhaitez extraire la représentation sous forme de chaîne et la stocker dans un, std::stringprocédez comme suit:

#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 trouve dans l'en- <sstream>tête.

blwy10
la source
2
C'est une manière ridicule et lourde pour obtenir une chaîne de sérialisation!
Gerd Wagner
9

La question a reçu une réponse. Mais je voulais ajouter un exemple concret.

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

Cet exemple nécessite de comprendre la surcharge de l'opérateur.


la source