开发者

Overloading << and >> in inherited classes

开发者 https://www.devze.com 2023-01-24 14:05 出处:网络
I have class Person (first name, last name, address, age) and overloaded operators << and >> to use it with filestreams:

I have class Person (first name, last name, address, age) and overloaded operators << and >> to use it with filestreams:

ostream& operator<< (ostream& outStream, Person& person)
{
  ...
}

istream& operator>> (istream& inStream, Person& person)
{
  ...
}

It works fine - I can read from and write to file easily, but I have added two classes inherited from Person: Student and Worker.

I wrote overloaded operators for them, very similar to those above:

ostream& operator<< (ostream& outStream, Worker& worker)
{
 ...
}

istream& operator>> (istream& inStream, Worker& worker)
{
 ...
}


ostream& operator<< (ostream& outStream, Student& student)
{
 ...
}

istream& operator>> (istream& inStream, Student& student)
{
 ...
}

Only difference are two more fields in each class. The problem is, that when I use overloaded operators with ei开发者_StackOverflowther Student or Worker it seems my compiler uses operators for person. Probably it makes hidden conversion from Student or Worker to Person, but as a result there are no those additional fields written to the file. offstream << People works just the same as offstream << Students or offstrem Workers. Maybe placing overloaded operators declarations for inherited classes first, and for Person later in the code would solve the issue, but i don't find it an elegant solution.

If you have some ideas how to manage with the problem above, I would appreciate.


Two things. First, make your operators take references to const, like this:

ostream& operator<< (ostream& outStream, const Person& person)

To solve your problem, a common pattern is to provide your types with a protected virtual toString method, and have the operator just call that. You can then just overload this method in the sub-classes, and even reuse the super-class implementation if you just want to append some values to the string.

Example:

class Person {
// other stuff
protected:
    virtual std::string toString();
    friend ostream& operator<< (ostream& outStream, const Person& person)
};

ostream& operator<< (ostream& outStream, Person& person)
{
    ostream << person.toString();
    return outStream;
}

Edit Actually, I like larsmans suggestion even better:

class Person {
// other stuff
protected:
    virtual void print(ostream & stream) const;
    friend ostream& operator<< (ostream& outStream, const Person& person)
};

ostream& operator<< (ostream& outStream, Person& person)
{
    person.print(outStream);
    return outStream;
}

This will be easier to implement than the toString idea, because you need no temporary stringstream or anything like that to build the string.


Most probably the code which calls the << operator accesses the object through a pointer or reference to Person, thus calling the operator for the Person type. The usual way of handling this is to provide a virtual method on the parent (i.e. Person) class which does the writing (getting the stream as an argument), and have the operator<< call this method to do the work. You need to provide the operator<< only for the parent class, the virtual dispatch mechanism will take care of choosing the right method for the object provided.

class Person {
   // ...
protected:
   virtual ostream& stream_write(ostream&) const;  //override this in child classes
   virtual istream& stream_read(istream&);        //this too
public:
   friend ostream& operator<< (ostream& stream, const Person& obj)
      { return obj.stream_write(stream); }
   friend istream& operator>> (istream& stream, Person& obj)
      { return obj.stream_read(stream); }
};


Your operators could call a virtual method. Something like this :

struct A
{
  virtual ~A(){}

  virtual std::ostream& Print( std::ostream &os ) const
  {
    // print what you want
    return os;
  }
};

struct B : A
{
  virtual ~B(){}

  virtual std::ostream& Print( std::ostream &os ) const
  {
    // print what you want
    return os;
  }
};

Then create the operator<< only for the base class :

std::ostream& operator<<( std::ostream &os, const A &a)
{
  return a.Print(os);
}


I usually call a virtual function streamIn and streamOut in the overloaded streaming operators, in that way I can actually create a template streaming function that works for all classes with streamIn and streamOut functions.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号