Переопределение операторов ввода и вывода
Операторы ввода >> и вывода << прекрасно работают для примитивных типов данных, таких как int или double. В то же время для использования их с объектами классов необходимо переопределять эти операторы.
Оператор <<
Обычно первый параметр оператора << представляет ссылку на неконстантный объект ostream. Данный объект не должен представлять константу, так как запись в поток изменяет его состояние. Причем параметр представляет именно ссылку, так как нельзя копировать объект класса ostream.
Второй параметр оператора определяется как ссылка на константу объекта класса, который надо вывести в поток.
Для совместимости с другими операторами переопределяемый оператор должен возвращать значение параметра ostream.
Также следует отметить, что операторы ввода и вывода не должны быть членами в классе, а определяются вне класса как обычные функции.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string>
struct Person
{
std::string name;
int age;
};
std::ostream& operator << (std::ostream &os, const Person &p)
{
return os << p.name << " " << p.age;
}
int main()
{
Person tom;
tom.name = "Tom";
tom.age = 31;
std::cout << tom << std::endl;
return 0;
}
В данном случае оператор вывода определяется для объектов структуры Person. Сам оператор по сути просто выводит имя и возраст пользователя через пробел:
Tom 31
Оператор >>
Первый параметр оператора >>, как правило, представляет ссылку на объект istream, с которого осуществляется чтение. Второй параметр представляет ссылку на неконстантный объект, в который надо считать данные.
Обычно в качестве результата операторы возвращают ссылку на поток ввода istream из первого параметра.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string>
struct Person
{
std::string name;
int age;
};
std::istream& operator >> (std::istream& in, Person& p)
{
in >> p.name >> p.age;
return in;
}
int main()
{
Person bob;
std::cout << "Input name and age: ";
std::cin >> bob;
std::cout << "Name: " << bob.name << "\tAge: " << bob.age << std::endl;
return 0;
}
Оператор ввода последовательно считывает из потока имя и возраст пользователя. При этом в данном случае предполагается, что имя представляет одно слово. Если стоит задача, считать сложное имя, которые состоит из нескольких слов, или имя и фамилию, то естественно надо определять более сложную логику.
Пример работы программы:
Input name and age: Bob 32
Name: Bob Age: 32
Однако что если мы введем для возраста вместо числа строку? В этом случае переменная age получит неопределенное значение. Существуют различные варианты, как обрабатывать подобные ситуации. Но в качестве примера мы можем в случае некорректного ввода устанавливать значение по умолчанию:
1
2
3
4
5
6
7
8
9
std::istream& operator >> (std::istream& in, Person& p)
{
in >> p.name >> p.age;
if (!in)
{
p = Person();
}
return in;
}
С помощью выражения if(!is) проверяем, является ли ввод неудачным. Если он неудачен, то используем конструктор без параметров для создания объекта.
Чтение и запись файла
Определив операторы ввода и выводы, мы можем их использовать также и для чтения и записи файла:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
class Person
{
public:
std::string name;
int age;
Person(std::string n, int a): name(n),age(a) { }
Person(){ }
};
std::ostream& operator << (std::ostream &os, const Person &p)
{
return os << p.name << " " << p.age;
}
std::istream& operator >> (std::istream& in, Person& p)
{
in >> p.name >> p.age;
if (!in)
{
p = Person();
}
return in;
}
int main()
{
// начальные данные - вектор объектов Person
std::vector<Person> people =
{
Person("Tom", 23),
Person("Bob", 25),
Person("Alice", 22),
Person("Kate", 31)
};
// запись данных в файл
std::ofstream out("D:\\users.txt");
if (out.is_open())
{
for (int i = 0; i < people.size(); i++)
{
out << people[i] << std::endl;
}
}
out.close();
// вектор для считываемых данных
std::vector<Person> users;
// чтение ранее записанных данных из файла
std::ifstream in("D:\\users.txt");
if (in.is_open())
{
Person p;
while (in >> p)
{
users.push_back(p);
}
}
in.close();
// вывод считанных данных на консоль
std::cout << "All users:" << std::endl;
for (int i = 0; i < users.size(); i++)
{
std::cout << users[i] << std::endl;
}
return 0;
}
Здесь для класса Person определены операторы ввода и вывода. С помощью оператора вывода данные будут записываться в файл users.txt, а с помощью оператора ввода - считываться из файла. В конце считанные данные выводятся на консоль:
Результат работы программы:
All users:
Tom 23
Bob 25
Alice 22
Kate 31