|
6.2. Перегрузка операторовПерегрузка операторов позволяет использовать для абстрактных типов данных привычные операторы, например + для сложения или объединения двух объектов, = для присвоения одного экземпляра объекта другому и т.д. Комплексная арифметика, матричная алгебра, символьные строки - это лишь немногие примеры, где удобно использовать перегруженные операторы. Например, для комплексных чисел (типа данных Complex) можно перегрузить следующие операторы:
Complex x,y,z; z=x*y; z=x.operator*(y); // явный вызов оператора class Complex { … Complex operator*(const Complex& other) const; … } Исходя из этой записи, нетрудно догадаться, как осуществляется перегрузка операторов для абстрактных типов данных. Просто нужно определить функцию-член с именем operator* или любой другой оператор. Рассмотрим пример класса Complex, а затем разберем подробно перегрузку некоторых типовых операторов. 6.2.1. Пример 6.1 (класс Complex (комплексное число))///////////////////////////////////////////////////////////////////////////// // Прикладное программирование // Пример 6.1. Класс Комплексное число // complex.h // // Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru // Университет ИТМО ///////////////////////////////////////////////////////////////////////////// // проверка на повторное подключение файла #if !defined COMPLEX_H #define COMPLEX_H #include <iostream> using namespace std; ///////////////////////////////////////////////////////////////////////////// // класс Комплексное число class Complex { private: // вещественная и мнимая часть комплексного числа double m_re, m_im; public: // конструкторы Complex(); Complex(double re, double im=0); Complex(const Complex& other); // получение параметров комплексного числа double GetRe() const; double GetIm() const; // изменение параметров комплексного числа void Set(double re, double im=0.); // оператор умножения Complex operator*(const Complex& other) const; // оператор умножения на число Complex operator*(const double& other) const; // оператор умножения с присваиванием Complex& operator*=(const Complex& other); // оператор присваивания Complex& operator=(const Complex& other); // оператор равенства bool operator== (const Complex& other) const; // оператор сопряжения комплексного числа Complex operator~() const; // унарный минус Complex operator-() const; // ввод/вывод комплексного числа friend ostream& operator<< (ostream& out, const Complex& x); friend istream& operator>> (istream& out, Complex& x); // преобразование типа Complex в double operator double() const; }; ///////////////////////////////////////////////////////////////////////////// // получение вещественной части комплексного числа inline double Complex::GetRe() const { return m_re; } ///////////////////////////////////////////////////////////////////////////// // получение мнимой части комплексного числа inline double Complex::GetIm() const { return m_im; } ///////////////////////////////////////////////////////////////////////////// // изменение параметров комплексного числа inline void Complex::Set(double re, double im) { m_re=re; m_im=im; } ///////////////////////////////////////////////////////////////////////////// #endif //defined COMPLEX_H
///////////////////////////////////////////////////////////////////////////// // Прикладное программирование // Пример 6.1. Класс Комплексное число // complex.cpp // // Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru // Университет ИТМО ///////////////////////////////////////////////////////////////////////////// // подключение описания класса #include "complex.h" ///////////////////////////////////////////////////////////////////////////// // конструктор по умолчанию Complex::Complex() : m_re(0.) , m_im(0.) { } ///////////////////////////////////////////////////////////////////////////// // полный конструктор Complex::Complex(double re, double im) : m_re(re) , m_im(im) { } ///////////////////////////////////////////////////////////////////////////// // конструктор копирования Complex::Complex(const Complex& x) : m_re(x.m_re) , m_im(x.m_im) { } ///////////////////////////////////////////////////////////////////////////// // оператор умножения Complex Complex::operator*(const Complex& other) const { return Complex(m_re*other.m_re-m_im*other.m_im, m_re*other.m_im-m_im*other.m_re); } ///////////////////////////////////////////////////////////////////////////// // оператор умножения на число Complex Complex::operator*(const double& other) const { return Complex(m_re*other, m_im*other); } ///////////////////////////////////////////////////////////////////////////// // оператор умножения с присваиванием Complex& Complex::operator*=(const Complex& other) { Complex temp(*this); m_re=temp.m_re*other.m_re - temp.m_im*other.m_im; m_im=temp.m_re*other.m_im + temp.m_im*other.m_re; return (*this); } ///////////////////////////////////////////////////////////////////////////// // унарный минус Complex Complex::operator-() const { return Complex(-m_re, -m_im); } ///////////////////////////////////////////////////////////////////////////// // оператор сопряжения комплексного числа Complex Complex::operator~() const { return Complex(m_re, -m_im); } ///////////////////////////////////////////////////////////////////////////// // оператор равенства bool Complex::operator== (const Complex& other) const { return (m_re == other.m_re && m_im == other.m_im); } ///////////////////////////////////////////////////////////////////////////// // оператор присваивания Complex& Complex::operator=(const Complex& other) { if(this != &other) { m_re=other.m_re; m_im=other.m_im; } return *this; } ///////////////////////////////////////////////////////////////////////////// // преобразование типа Complex в double Complex::operator double() const { return (m_re*m_re-m_im*m_im); } ///////////////////////////////////////////////////////////////////////////// // вывод комплексного числа на экран ostream& operator<< (ostream& out, const Complex& x) { return (out<<"("<<x.m_re<<","<<x.m_im<<")"); } ///////////////////////////////////////////////////////////////////////////// // ввод комплексного числа с клавиатуры istream& operator>> (istream& in, Complex& x) { return (in>>x.m_re>>x.m_im); } /////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////// // Прикладное программирование // Пример 6.1. Класс Комплексное число // test_complex.cpp // // Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru // Университет ИТМО ///////////////////////////////////////////////////////////////////////////// #include <iostream> using namespace std; // подключение описания класса #include "complex.h" ///////////////////////////////////////////////////////////////////////////// // пример использования класса Complex void main() { Complex x(1,1), y(2,2), res1, res2, res3; Complex x1; // тестирование преобразования типов // преобразование вещественного числа в комплексное при помощи конструктора res1=Complex(3.14); cout<<"Complex(3.14): "<<res1<<endl; // преобразование комплекcного числа в вещественное при помощи перегруженного оператора double double c=double(res1); cout<<"double(res1): "<<c<<endl<<endl; // тестирование арифметических операторов res1=x*y; // перемножение двух комплексных чисел res1=x.operator*(y); // то же самое, явный вызов оператора res2=-x; // унарный минус res3=~x; // комлексное сопряжение x*=y; // умножение с присваиванием cout<<" x*y="<<res1<<endl; cout<<" -x="<<res2<<endl; cout<<" ~x="<<res3<<endl; cout<<"x*=y: "<<x<<endl<<endl; // тестирование оператора равенства if(x==y) cout<<x<<" = "<<y<<endl<<endl; else cout<<x<<" != "<<y<<endl<<endl; // тестирование операторов присваивания res3=res2=res1=1; cout<<res1<<"="<<res2<<"="<<res3<<endl; } ///////////////////////////////////////////////////////////////////////////// 6.2.2. Перегрузка бинарных операторов// оператор умножения Complex Complex::operator*(const Complex& other) const { return Complex(m_re*other.m_re-m_im*other.m_im, m_re*other.m_im-m_im*other.m_re); } // оператор умножения на число Complex Complex::operator*(const double& other) const { return Complex(m_re*other, m_im*other); } // пример использования Complex x, z; double y; z=x*y; 6.2.3. Перегрузка унарных операторов// унарный минус Complex Complex::operator-() const { return Complex(-m_re, -m_im); } // оператор сопряжения комплексного числа Complex Complex::operator~() const { return Complex(m_re, -m_im); } // пример использования Complex x, y; y=-x; y=~x; 6.2.4. Перегрузка логических операторов// оператор равенства bool Complex::operator== (const Complex& other) const { return (m_re == other.m_re && m_im == other.m_im); } // пример использования Complex x, y; if(x==y) { … } 6.2.5. Перегрузка оператора присваиванияПерегрузку оператора присваивания следует выделить особо. Мы уже рассматривали функцию, которая позволяет инициализировать один экземпляр класса значениями переменных членов хранящихся в другом экземпляре. Это конструктор копирования. Оператор присваивания отличается тем, что новый класс при его выполнении не создаётся, а значения членов одного экземпляра присваиваются членам другого экземпляра. При перегрузке оператора присваивания необходимо руководствоваться следующими правилами:
// оператор присваивания Complex& Complex::operator=(const Complex& other) { if(this != &other) { m_re=other.m_re; m_im=other.m_im; } return *this; } // пример использования Complex x, y, z; x=y=z=1; 6.2.6. Перегрузка операторов с присваиваниемПерегрузка операторов с присваиванием (+=, *= и т.д.) осуществляется по следующим правилам:
// оператор умножения с присваиванием Complex& Complex::operator*=(const Complex& other) { Complex temp(*this); m_re=temp.m_re*other.m_re - temp.m_im*other.m_im; m_im=temp.m_re*other.m_im + temp.m_im*other.m_re; return (*this); } // пример использования Complex x, y; x*=y; 6.2.7. Перегрузка преобразования типовПреобразовать встроенный тип данных к нашему абстрактному типу можно при помощи конструктора (см. раздел 6.1.2). А чтобы преобразовать абстрактный тип данных к встроенному типу, необходимо перегрузить оператор: // преобразование типа Complex в double Complex::operator double() const { return (m_re*m_re-m_im*m_im); } // пример использования Complex x; double y; y=double(x); 6.2.8. Перегрузка оператора доступа по индексуДля некоторых классов, которые хранят массивы (например, матрица) для удобного доступа к элементу массива по его индексу можно перегрузить оператор (): // оператор доступа по индексу double& matrix::operator() (int i, int j) { return (p[i][j]); // или p[i*size+j]; } // пример использования matrix x; double y; y=matrix(1,1); // доступ к элементу (1,1) 6.2.9. Перегрузка операторов ввода/выводаПерегрузку операторов ввода/вывода приходится оформлять в виде дружественных функций класса. Это происходит из-за того, что при использовании оператора ввода или вывода слева от него должен находиться экземпляр потока ввода/вывода, то есть перегруженные операторы ввода/вывода являются членами потоков, а не других классов. Вмешаться во внутреннюю реализацию потоков мы не можем, но можем использовать функции, которые не являются членами классов, но позволяют получить доступ к их скрытым (private) членам. // oписание: friend ostream& operator<< (ostream& out, const Complex& x); friend istream& operator>> (istream& out, Complex& x); // вывод комплексного числа на экран ostream& operator<< (ostream& out, const Complex& x) { return (out<<"("<<x.m_re<<","<<x.m_im<<")"); } // ввод комплексного числа с клавиатуры istream& operator>> (istream& in, Complex& x) { return (in>>x.m_re>>x.m_im); } // пример использования Complex x; cout<<x<<endl; 6.2.10. Неперегружаемые операторыНе могут быть перегружены следующие операторы:
Кроме того, с помощью механизма перегрузки можно переопределить только существующие операторы. Новые операторы определить невозможно. |