Переопределение операторов ввода и вывода

Последнее обновление: 31.03.2023

Операторы ввода >> и вывода << прекрасно работают для примитивных типов данных, таких как int или double. В то же время для использования их с объектами классов необходимо переопределять эти операторы.

Оператор <<

Стандартный выходной поток cout имеет тип std::ostream. Поэтому первый параметр (левый операнд) операции << представляет ссылку на неконстантный объект ostream. Данный объект не должен представлять константу, так как запись в поток изменяет его состояние. Причем параметр представляет именно ссылку, так как нельзя копировать объект класса ostream.

Второй параметр оператора определяется как ссылка на константный объекта класса, который надо вывести в поток.

Для совместимости с другими операторами переопределяемый оператор должен возвращать значение параметра std::ostream.

Также следует отметить, что операторы ввода и вывода не должны быть членами в классе, а определяются вне класса как обычные функции.

#include <iostream>
 
class Person 
{
public:
    Person(std::string name, unsigned age): name{name}, age{age} {}
    std::string getName() const {return name;}
    unsigned getAge() const {return age;}

    void setName(std::string personName){ name = personName;}
    void setAge(unsigned personAge){ age = personAge;}
private:
    std::string name;
    unsigned age;
};
std::ostream& operator << (std::ostream &os, const Person &person)
{
    return os << person.getName() << " " << person.getAge();
}
int main()
{
    Person tom{"Tom", 38};
    std::cout << tom << std::endl;

    Person bob{"Bob", 42};
    std::cout << bob << std::endl;
}

В данном случае оператор вывода определяется для объектов структуры Person. Сам оператор по сути просто выводит имя и возраст пользователя через пробел. Консольный вывод программы:

Tom 38
Bob 42

Оператор >>

Первый параметр оператора >> представляет ссылку на объект istream, с которого осуществляется чтение. Второй параметр представляет ссылку на неконстантный объект, в который надо считать данные. В качестве результата оператор возвращают ссылку на поток ввода istream из первого параметра.

#include <iostream>
 
class Person 
{
public:
    Person(std::string name, unsigned age): name{name}, age{age} {}
    std::string getName() const {return name;}
    unsigned getAge() const {return age;}

    void setName(std::string personName){ name = personName;}
    void setAge(unsigned personAge){ age = personAge;}
private:
    std::string name;
    unsigned age{};
}; 
std::istream& operator >> (std::istream& in, Person& person)
{
    std::string name;
    unsigned age;
    in >> name >> age;
    person.setName(name);
    person.setAge(age);
    return in;
}
int main()
{
    Person bob{"",0};
    std::cout << "Input name and age: ";
    std::cin >> bob;
    std::cout << "Name: " << bob.getName() << "\tAge: " << bob.getAge() << std::endl;
}

Оператор ввода последовательно считывает из потока данные в переменные name и age и затем использует их для установки имени и возраста пользователя.

std::istream& operator >> (std::istream& in, Person& person)
{
    std::string name;
    unsigned age;
    in >> name >> age;
    person.setName(name);
    person.setAge(age);
    return in;
}

При этом в данном случае предполагается, что имя представляет одно слово. Если надо считать сложное имя, которое состоит из нескольких слов, или имя и фамилию, то естественно надо определять более сложную логику.

Пример работы программы:

Input name and age: Bob 42
Name: Bob	Age: 42

Однако что если мы введем для возраста вместо числа строку? В этом случае переменная age получит неопределенное значение. Существуют различные варианты, как обрабатывать подобные ситуации. Но в качестве примера мы можем в случае некорректного ввода устанавливать значение по умолчанию:

std::istream& operator >> (std::istream& in, Person& person)
{
    std::string name;
    unsigned age;
    in >> name >> age;
    if (in)    
	{
        person.setName(name);
        person.setAge(age);
    }
    return in;
}

С помощью выражения if(in) проверяем, является ли ввод удачным. Если он завершился успешно, то устанавливаем введенные значения. Если же ввод не удался, у объекта Person остаются те значения, которые у него было до ввода.

Чтение и запись файла

Определив операторы ввода и выводы, мы можем их использовать также и для чтения и записи файла:

#include <iostream>
#include <fstream>
#include <vector>

class Person 
{
public:
    Person(std::string name, unsigned age): name{name}, age{age} {}
    std::string getName() const {return name;}
    unsigned getAge() const {return age;}

    void setName(std::string personName){ name = personName;}
    void setAge(unsigned personAge){ age = personAge;}
private:
    std::string name;
    unsigned age{};
};
std::ostream& operator << (std::ostream &os, const Person &person)
{
    return os << person.getName() << " " << person.getAge();
}
std::istream& operator >> (std::istream& in, Person& person)
{
    std::string name;
    unsigned age;
    in >> name >> age;
    // если ввод не удался, устанавливаем некоторые значения по умолчанию
    if (in)    
	{
        person.setName(name);
        person.setAge(age);
    }
    return in;
}
int main()
{
    // начальные данные - вектор объектов Person
    std::vector<Person> people =
    {
        Person{"Tom", 23},
        Person{"Bob", 25},
        Person{"Alice", 22},
        Person{"Kate", 31}
    };
    // запись данных в файл
    std::ofstream out("people.txt");
    if (out.is_open())
    {
        for (const Person& person: people)
        {
            out << person << std::endl;
        }
    }
    out.close();
    // вектор для считываемых данных
    std::vector<Person> new_people;
    // чтение ранее записанных данных из файла
    std::ifstream in("people.txt");
    if (in.is_open())
    {
        Person person{"",0};
        while (in >> person)
        {
            new_people.push_back(person);
        }
    }
    in.close();
    // вывод считанных данных на консоль
    std::cout << "All people:" << std::endl;
    for (const Person& person: new_people)
    {
        std::cout << person << std::endl;
    }
}

Здесь для класса Person определены операторы ввода и вывода. С помощью оператора вывода данные будут записываться в файл users.txt, а с помощью оператора ввода - считываться из файла. В конце считанные данные выводятся на консоль:

Результат работы программы:

All users:
Tom 23
Bob 25
Alice 22
Kate 31
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850