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

12,737

Solution 1

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

Solution 2

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.

And this is good thing. The smaller your class is the better. If popular C++ idiom allows you to have one more thing out of your class why not follow it?

Now i wonder if there are any drawbacks of writing a toString() method

The drawbacks are:

  • operator<< works in uniform way with builtin types (like int) and user-defined types. toString could be available for classes only
  • C++ is more heterogeneous than Java. std::string is the most popular string, but still there exist other string classes and they are used.
  • the string must be created, which potentially might come with a performance hit. If you write to stream directly you avoid it.

Solution 3

They are fundamentally different things. Providing a operator<< overload effectively extends the interface of the stream, making objects of your class type streamable. Providing a toString function extends the interface of your class, making it possible to get a std::string from your class. These represent different things.

The interface of your class should entirely correspond to what it represents in your program logic (single responsibility). Rarely is toString a natural part of a class's interface. However, extending the interface of the stream to accept more objects is much more logical.

That is, in one case you are saying "Now you can stream objects of this class type". In the other case you are saying "You can turn objects of this class type into a std::string." - it just so happens that that std::string is then streamable. Think about it - does it really make sense for my Person class to have a toString function? Since when have I been able to turn people into text?

Solution 4

Your first assumption is wrong. You don't need to make any changes in ostream.

An operator method like operator<< can be defined in two ways: As a method of the ostream class, taking your x object as a parameter, or as a plain old function with two parameters, taking an ostream as the first parameter, and your x object as the second parameter.

Share:
12,737

Related videos on Youtube

463035818_is_not_a_number
Author by

463035818_is_not_a_number

BASIC was my first love. Why is “using namespace std;” considered bad practice? Why should I not #include &lt;bits/stdc++.h&gt;? Can a local variable's memory be accessed outside its scope? Why aren't variable-length arrays part of the C++ standard? Why should C++ programmers minimize use of 'new'? Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong? What is the XY problem? Why is “Can someone help me?” not an actual question? How do I ask and answer homework questions? What is a debugger and how can it help me diagnose problems?

Updated on June 14, 2022

Comments

  • 463035818_is_not_a_number
    463035818_is_not_a_number almost 2 years

    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;
    
  • 463035818_is_not_a_number
    463035818_is_not_a_number over 9 years
    I guess whether it makes sense is a matter of taste. In my opinion it completely makes sense that any Java object can be converted to a string via toString()
  • 463035818_is_not_a_number
    463035818_is_not_a_number over 9 years
    what about abstract classes? I can have a toString()=0; but how do I declare that any child class should come with an overloaded << operator?
  • 463035818_is_not_a_number
    463035818_is_not_a_number over 9 years
    maybe i should have first read the link you posted ;) There its the first point saying that "if (f needs to be virtual) make f a member function".
  • bames53
    bames53 over 9 years
    @tobi303 Yeah, for now virtual functions must be members. It's possible that non-member functions may gain support for virtual dispatch in the future: stroustrup.com/multimethods.pdf
  • Gerd Wagner
    Gerd Wagner about 8 years
    These cons have much less weight that the pros of having a uniform serialization method encapuslated with any class. Why should we care about bad practices like not using std::string?