C++: как правильно переопределить << и >> в классе?
Мне требовался несложный шаблон класса, работающий более чем с одним скалярным типом данных, но гораздо более компактный, чем вот этот код.
В данном случае класс - простое "кольцо" элементов с фиксированным размером и двумя базовыми действиями - "положить элемент в кольцо и сдвинуть указатель", "забрать элемент из кольца и сдвинуть указатель", например:
было 0 0 0 0 0 (размерность кольца равна 5 элементов)
положили 1 2 3
стало 0 0 1 2 3
положили 4 5 6
стало 2 3 4 5 6 (указатель перед выводом стоял на "2", а "1" уже затёрлась)
взяли 3 элемента, выводя их, вывелись 2 3 4
снова вывели кольцо, вывелось 5 6 2 3 4
Ring <int> A(5); A.put(1); A.put(2); A.put(3); cout << A; A.put(4); A.put(5); A.put(6); cout << A; cout << endl << A.get(); cout << endl << A.get(); cout << endl << A.get(); cout << A;
Выше показан код из main
, который мог бы дать такую картину (модифицированной версией программы!), а ниже - полный листинг программы (консоль актуальной сборки Visual Studio 2019).
#include <iostream> #include <cstdlib> using namespace std; template <typename T> class Ring { //Шаблон класса "кольцо" private: T* data; unsigned size, pointer; public: Ring(int size = 2) { if (size < 2) size = 2; this->data = new T[size]; if (!data) { cout << "Memory allocation error!"; exit(1); } this->size = size; this->pointer = 0; for (unsigned u = 0; u < size; u++) this->data[u] = (T)0; } void put(T val) { this->data[this->pointer] = val; this->pointer++; if (this->pointer == this->size) this->pointer = 0; } T get() { T val = this->data[this->pointer]; this->pointer++; if (this->pointer == this->size) this->pointer = 0; return val; } }; int main() { Ring <int>* R1 = new Ring <int>(5); //Кольцо целых чисел в "куче" R1->put(1); R1->put(2); R1->put(3); cout << endl; for (unsigned u = 0; u < 6; u++) cout << R1->get() << " "; //0 0 1 2 3 0 Ring <double> R2 (3); //Кольцо вещественных чисел в "стеке" R2.put(1.5); R2.put(2.5); cout << endl; for (unsigned u = 0; u < 3; u++) cout << R2.get() << " "; //0 1.5 2.5 return 0; }
В этом коде нет возможности вывести всё кольцо в поток "сразу", да ещё и конструкции вида
cout << endl << R2.get() << " " << R2.get() << " " << R2.get();
нормально работать не будут из-за пресловутых точек следования.
Поэтому ниже приводится расширенный шаблон, в который включены шаблоны операторных функций << и >> для поточного вывода и ввода.
#include <iostream> #include <cstdlib> using namespace std; template <typename T> class Ring { //Шаблон класса "кольцо" private: //Данные и шаблоны операторов ввода-вывода T* data; unsigned size, pointer; template <typename T1> friend ostream& operator << (ostream& os, const Ring <T1>&); template <typename T1> friend istream& operator >> (istream& is, Ring <T1>&); public: Ring(unsigned size = 2) { //Конструктор if (size < 2) size = 2; this->data = new T[size]; if (!data) { cout << "Memory allocation error!"; exit(1); } for (unsigned u = 0; u < size; u++) this->data[u] = (T)0; this->size = size; this->pointer = 0; } void put(T val) { //Положить элемент в кольцо и сдвинуть указатель this->data[this->pointer] = val; this->pointer++; if (this->pointer == this->size) this->pointer = 0; } T get() { //Забрать элемент из кольца и сдвинуть указатель T val = this->data[this->pointer]; this->pointer++; if (this->pointer == this->size) this->pointer = 0; return val; } }; template <typename T> ostream& operator << (ostream& os, const Ring <T>& dt) { //Шаблон оператора вывода << os << std::endl; unsigned index = dt.pointer; for (unsigned k = 0; k < dt.size; k++) { os << dt.data[index] << (k == dt.size - 1 ? "" : " "); index++; if (index == dt.size) index = 0; } return os; } template <typename T> istream& operator >> (istream& is, Ring <T>& dt) { //Шаблон оператора ввода >> is >> dt.data[dt.pointer]; dt.pointer++; if (dt.pointer == dt.size) dt.pointer = 0; return is; } int main() { Ring <int>* R1 = new Ring <int>(3); //Создали объект в куче R1->put(1); R1->put(2); R1->put(3); cout << *R1; Ring <double> R2(3); //Создали объект в стеке cout << endl << "Input the double number: "; cin >> R2; R2.put(1.5); R2.put(2.5); cout << R2; return 0; }
Обратите внимание, что в описании шаблонов операторов << и >> для класса указан "другой" тип данных T1
, без этого ничего работать не будет. Модификатор const
и оператор ссылки &
также имеют значение.
Важно, что тем же кодом можно обмениваться данными с файлами, например:
int main() { Ring <int> R (5); ifstream ("input.txt") >> R >> R >> R >> R >> R; //добавить #include <fstream> ofstream("output.txt") << R; return 0; }
В файле input.txt
из текущей папки было
1 2 3 4 5
В файл output.txt
, созданный в текущей папке, записалось
1 2 3 4 5
21.01.2021, 20:09 [799 просмотров]