Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ What is wrong with using a toString() method

I just came across this question which is about how to be able to print an object via

std::cout << x << std::endl;

As I understood, the standard way to accomplish this is to overload the ostreams << operator. However, this is adding a feature to the ostream rather than to my class.

The alternative (also given as answer to the above mentioned question) is to override the string conversion operator. However, this comes with the warning of leading to "unintentional conversions and hard-to-trace bugs".

Now i wonder if there are any drawbacks of writing a toString() method and then using it via

std::cout << x.toString() << std::endl;
like image 909
463035818_is_not_a_number Avatar asked Dec 03 '14 10:12

463035818_is_not_a_number


1 Answers

Output streams handle output formatting as well as output. So with your toString() method clients won't be able to manage the formatting for the object the way they do for everything else:

// set specific formatting options for printing a value
std::cout << std::scientific << std::setprecision(10) << 10.0 << '\n'; // prints 1.0000000000e+01

// set formatting based on user's cultural conventions
std::cout.imbue(std::locale(""));
std::cout << 10000000 << '\n'; // depending on your system configuration may print "10,000,000"

Perhaps you don't care to allow any formatting so perhaps this won't matter.

Another consideration is that outputting to a stream doesn't require that the entire string representation be in memory at once, but your toString() method does.


Others have pointed this out, but I think a clearer way of saying it is that your classes interface is not limited to just the methods it provides, but also includes the other functions you build around it, including non-member functions such as the operator<< overloads you provide. Even though it's not a method of your class you should still think of it as part of your class's interface.

Here's an article that talks about this which perhaps you will find helpful: How Non-Member Functions Improve Encapsulation


Here's a simple example of overloading operator<< for a user defined class:

#include <iostream>

struct MyClass {
  int n;
};

std::ostream &operator<< (std::ostream &os, MyClass const &m) {
  for (int i = 0; i < m.n; ++i) {
    os << i << ' ';
  }
  return os;
}

int main() {
  MyClass c = {1000000};
  std::cout << c << '\n';
}
like image 69
bames53 Avatar answered Sep 22 '22 06:09

bames53