Перегрузка << и >> в унаследованных классах - PullRequest
3 голосов
/ 12 ноября 2010

У меня есть класс Person (имя, фамилия, адрес, возраст) и перегруженные операторы << и >>, чтобы использовать его с файловыми потоками:

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

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

Работает нормально - я могу читать из илегко записывать в файл, но я добавил два класса, унаследованных от Person: Student и Worker.

Я написал для них перегруженные операторы, очень похожие на приведенные выше:

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

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


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

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

Разница только в том,еще два поля в каждом классе.Проблема в том, что, когда я использую перегруженные операторы со студентом или рабочим, кажется, что мой компилятор использует операторы для человека.Возможно, он делает скрытое преобразование из ученика или работника в личность, но в результате в файл не записываются эти дополнительные поля.offstream << Люди работают так же, как offstream << Студенты или внешние рабочие.Возможно, размещение объявлений перегруженных операторов сначала для унаследованных классов, а для Person позже в коде решит проблему, но я не нахожу это элегантным решением. </p>

Если у вас есть идеи, как справиться с проблемойвыше, я был бы признателен.

Ответы [ 4 ]

12 голосов
/ 12 ноября 2010

Две вещи. Во-первых, заставьте ваших операторов принимать ссылки на const, например так:

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

Чтобы решить вашу проблему, обычным делом является предоставление вашим типам защищенного виртуального toString метода, а оператор просто вызывает его. Затем вы можете просто перегрузить этот метод в подклассах и даже повторно использовать реализацию суперкласса, если вы просто хотите добавить некоторые значения в строку.

Пример:

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

Редактировать На самом деле, мне нравится предложение Ларсмана еще лучше:

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

Это будет легче реализовать, чем идея toString, потому что вам не нужен временный stringstream или что-то подобное для построения строки.

3 голосов
/ 12 ноября 2010

Скорее всего, код, который вызывает оператор <<, обращается к объекту через указатель или ссылку на Person , вызывая оператор для типа Person . Обычный способ справиться с этим - предоставить виртуальный метод в родительском (то есть Person ) классе, который выполняет запись (получая поток в качестве аргумента), и operator<< вызывает этот метод для выполнения работа. Вы должны предоставить operator<< только для родительского класса, механизм виртуальной диспетчеризации позаботится о выборе правильного метода для предоставленного объекта.

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); }
};
2 голосов
/ 12 ноября 2010

Ваши операторы могут вызывать виртуальный метод.Примерно так:

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

Затем создайте оператор << только для базового класса: </p>

std::ostream& operator<<( std::ostream &os, const A &a)
{
  return a.Print(os);
}
0 голосов
/ 12 ноября 2010

Я обычно вызываю виртуальную функцию streamIn и streamOut в перегруженных потоковых операторах, таким образом я могу фактически создать потоковую функцию шаблона, которая работает для всех классов с функциями streamIn и streamOut.

...