C ++ odpowiednik metody toString w Javie?

151

Chciałbym kontrolować, co jest zapisywane w strumieniu, tj. coutDla obiektu klasy niestandardowej. Czy to możliwe w C ++? W Javie można by nadpisać toString()metodę w podobnym celu.

Bogdan Balan
źródło

Odpowiedzi:

176

W C ++ możesz przeciążać operator<<dla ostreami swoją klasę niestandardową:

class A {
public:
  int i;
};

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

W ten sposób możesz wyświetlać instancje swojej klasy w strumieniach:

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

Jeśli operator<<chcesz wydrukować wewnętrzne elementy klasy Ai naprawdę potrzebujesz dostępu do jej prywatnych i chronionych członków, możesz również zadeklarować to jako funkcję znajomego:

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 << ")";
}
sth
źródło
16
Lepiej jest zadeklarować operator << jako funkcję zaprzyjaźnioną klasy, ponieważ może to być wymagane w celu uzyskania dostępu do prywatnych elementów członkowskich klasy.
Naveen,
5
Jeszcze lepiej zadeklaruj go jako friend, a także wewnątrz ciała klasy - dzięki temu nie będziesz musiał robić using namespacedla przestrzeni nazw zawierającej operator (i klasę), ale ADL znajdzie go tak długo, jak obiekt tej klasy jest jeden z operandów.
Pavel Minaev,
... powyższe miało na celu powiedzenie „ zdefiniuj go jako przyjaciela w treści klasy” - tak jak w definicji elementu wbudowanego.
Pavel Minaev
2
@fnieto: ta dumpmetoda publiczna jest brudna i niepotrzebna. Używanie friendtutaj jest całkowicie w porządku. To, czy wolisz metodę zbędną, czy natrętną, friendjest całkowicie kwestią gustu, chociaż friendprawdopodobnie została wprowadzona właśnie w tym celu.
Konrad Rudolph
1
@Pavel: wyszukiwanie zależne od argumentów i tak je znajdzie, o ile operator jest zdefiniowany w tej samej przestrzeni nazw co klasa. Nie ma to nic wspólnego z przyjaciółmi i nie musi być deklarowane / definiowane w klasie. Ponadto utworzenie operator<<()funkcji składowej nie zadziała: musiałbyś uczynić ją funkcją składową std::ostream, aby akceptowała operand typu po lewej stronie std::ostream.
sth
50

Możesz to również zrobić w ten sposób, pozwalając na polimorfizm:

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
źródło
3
+1 dla funkcji wirtualnej, aby skopiować toStringzachowanie Javy .
Konrad Rudolph
Dlaczego głupi zamiast bezpośrednio podawać operator << w klasie?
zakonnicy
1
ponieważ nie chcesz mieć nieskończonej pętli i awarii
fnieto - Fernando Nieto
1
Być może ta technika jest szybka i łatwa do przekazywania opcji serializacji. W przeciwnym razie konieczne byłoby zdefiniowanie innego operatora przyjaznego dla klas <<, który jest inicjowany z opcjami i danymi do serializacji.
Samuel Danielson
Inną kwestią byłoby to, że implementacja funkcji zrzutu mogłaby być wymuszona przez interfejs, co nie byłoby możliwe przy użyciu proponowanego operatora.
jupp0r
29

W C ++ 11 to_string jest ostatecznie dodawane do standardu.

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

Zhaojun Zhang
źródło
15
Jest to przydatny dodatek do tej strony, jednak implementacja C ++ znacznie różni się od tej w Javie / C #. W tych językach ToString()jest funkcją wirtualną zdefiniowaną w klasie bazowej wszystkich obiektów i dlatego jest używana jako standardowy sposób wyrażania reprezentacji ciągu dowolnego obiektu. Te funkcje std::stringmają zastosowanie tylko do typów wbudowanych. Idiomatycznym sposobem w C ++ jest przesłonięcie <<operatora dla typów niestandardowych.
Drew Noakes
9
„Brzydota” standardowej sygnatury operator<<, w porównaniu do prostej Stringsemantyki Javy, skłania mnie do uwagi, że to_string()jest to nie tylko „przydatny dodatek”, ale nowy preferowany sposób robienia tego w C ++. Jeśli, tak jak w przypadku OP, Apożądana jest niestandardowa reprezentacja klasy w postaci łańcucha , wystarczy napisać string to_string(A a)poniższą definicję class Awystarczającą. To propaguje się z dziedziczeniem, jak w Javie i może być łączone (przez dodawanie ciągów), jak w Javie. Brak zastępowania toString()w Javie i tak ma ograniczone zastosowanie.
P Marecki
10

Jako rozszerzenie tego, co powiedział John, jeśli chcesz wyodrębnić reprezentację ciągu i zapisać ją w pliku std::stringzrób to:

#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::stringstreamznajduje się w <sstream>nagłówku.

blwy10
źródło
2
To absurdalny i niewygodny sposób uzyskiwania ciągu serializacji!
Gerd Wagner
9

Odpowiedź na pytanie. Ale chciałem dodać konkretny przykład.

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

Ten przykład wymaga zrozumienia przeciążenia operatora.


źródło