C ++ эквивалент ToString Java?


151

Я хотел бы контролировать то, что записывается в поток, т. Е. coutДля объекта пользовательского класса. Это возможно в C ++? В Java вы можете переопределить toString()метод для аналогичной цели.

Ответы:


176

В C ++ можно перегрузить operator<<для ostreamи пользовательского класса:

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

16
Лучше объявить operator << как функцию друга класса, так как это может потребоваться для доступа к закрытым членам класса.
Навин

5
Еще лучше объявить его как friend, а также внутри тела класса - при этом вам не придется делать это using namespaceдля пространства имен, содержащего оператор (и класс), но ADL найдет его, пока объект этого класса один из операндов.
Павел Минаев

... вышесказанное должно было сказать « определите его как друга внутри тела класса» - как в определении встроенного члена.
Павел Минаев

2
@fnieto: этот dumpпубличный метод грязный и ненужный. Использование friendздесь прекрасно. Предпочитаете ли вы избыточный метод или навязчивый friend- это дело вкуса, хотя friend, возможно, оно было введено именно для этой цели.
Конрад Рудольф

1
@Pavel: поиск, зависящий от аргумента, все равно найдет его, если оператор определен в том же пространстве имен, что и класс. Это не имеет ничего общего с друзьями и не нуждается в том, чтобы это было объявлено / определено внутри класса. Кроме того, создание operator<<()функции-члена не будет работать: вам придется сделать ее функцией-членом, std::ostreamчтобы она принимала левый операнд типа std::ostream.
STH

50

Вы также можете сделать это таким образом, разрешив полиморфизм:

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

3
+1 для виртуальной функции, чтобы скопировать toStringповедение Java .
Конрад Рудольф

Почему глупее, чем прямо указывать оператор << в классе?
монахи

1
потому что вы не хотите иметь бесконечный цикл и сбой
fnieto - Фернандо Ньето

1
Возможно, эта техника быстрая и простая для передачи опций о том, что сериализировать. В противном случае потребуется определить другой оператор связи класса <<, который инициализируется опциями и данными для сериализации.
Сэмюэль Даниэльсон

Другой момент заключается в том, что реализация функциональности дампа может быть реализована с помощью интерфейса, что будет невозможно при использовании предложенного оператора.
jupp0r

29

В C ++ 11 to_string наконец добавляется в стандарт.

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


15
Это полезное дополнение к этой странице, однако реализация C ++ значительно отличается от реализации в Java / C #. В этих языках ToString()это виртуальная функция, определенная в базовом классе всех объектов, и поэтому она используется как стандартный способ выражения строкового представления любого объекта. Эти функции std::stringотносятся только к встроенным типам. Идиоматический способ в C ++ - переопределить <<оператор для пользовательских типов.
Дрю Ноакс

9
«Уродство» стандартной сигнатуры по operator<<сравнению с простой Stringсемантикой Java заставляет меня заметить, что to_string()это не только «полезное дополнение», но и новый предпочтительный способ сделать это в C ++. Если, как и в случае с OP, Aжелательно пользовательское строковое представление класса , достаточно просто написать приведенное string to_string(A a)ниже определение class A. Это распространяется с наследованием, как в Java, и может быть объединено (путем добавления строки), как в Java. Не переопределенный toString()в Java в любом случае имеет ограниченное использование.
П Марецки

10

В качестве дополнения к тому, что сказал Джон, если вы хотите извлечь строковое представление и сохранить его в 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>шапке.


2
Это нелепый и громоздкий способ получения строки сериализации!
Герд Вагнер

9

На вопрос был дан ответ. Но я хотел бы добавить конкретный пример.

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

Этот пример требует понимания перегрузки оператора.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.