Перегруженные операторы ввода-вывода « и »
Сохраняя дух ООП, важно перегрузить оператор «, чтобы выводить пользовательские типы, так же как и собственные. Оператор « имеет два аргумента: ostream& и АТД, и должен создавать ostream&. Используется ссылка на поток и возвращается ссылка на поток (вне зависимости от того, что перегружается — « или »), потому, что мы не хотим копировать объект потока. Напишем эти функции для типа rational.
В файле rational.cpp
class rational{
public:
friend ostream& operator«(ostream& out, rational x) ;
friend istream& operator»(istream& in, rational& x) ;
. . . . .
private:
long a, q;
};
ostream& operator«(ostream& out, rational x)
{
return (out « x.a « " / " « x.q « '\t');
}
Перегрузка оператора » для вывода пользовательского типа обычно выглядит так:
istream& operator»(istream& р, пользовательский_тип& x)
Если функции необходим доступ к закрытым членам x, ее надо сделать дружественной к этому классу. Ключевой момент здесь — сделать х параметром-ссылкой, чтобы его значение можно было изменять. Для этого в классе rational необходимо поместить объявление friend для этого оператора и обеспечить определение его функции:
istream& operator»(istream& in, rational& x)
{
return (in » x.a » x.q);
}
Перегрузка оператора ( ) для индексирования
Матричный тип, предоставляющий динамически размещаемые двумерные массивы, может быть разработан с оператором вызова функции для обеспечения выбора элемента. Это хороший пример контейнерного класса, который полезен в научных других расчетах.
Оператор вызова функции () перегружается как нестатическая функция-член. Он может быть перегружен для различных сигнатур. Он часто используется в oпeрациях итератора (см. упражнения с 12, стр. 222 по 14, стр. 223) или в тех операциях, где нужны множественные индексы (см. упражнение 8, на стр. 228, для операции с подстроками).
В файле matrix3.cpp
//Тип для динамической матрицы
class matrix {
public:
matrix(int с, int r);
~matrix();
doubles operator()(int i, int j);
matrix& operator=(const matrix& m);
matrix& operator+= (matrix& m);
private:
int c_size, r_size;
double **p;
};
matrix::matrix(int c, int r) : c_size(c), r_size(r)
{
p = new double*[c] ;
assert(p != 0);
for (int i = 0; i <c; ++i){
p[i] = new double[r];
assert(p[i] i= 0);
}
}
matrix:: ~matrix()
{
for (int i = 0; i < c_size; ++i)
delete [] p[i] ;
delete [] p;
}
inline double& matrix::operator()(int i, int j)
{
assert ( i >= 0 && i < c_size && j >= 0 && j < r_size) ;
return p[i][j];
}
matrix& matrix::operator=(const matrix& m)
{
assert(m.c_size = = c_size && m.r_size = = r_size);
int i , j ;
for (i = 0; i < c_size; ++i)
for (j = 0; j < r_size; ++j)
p[i] [jl = m.p[i][j];
return (*this) ;
}
matrix& matrix::operator+=(matrix& m)
{
assert(m.c_size == c_size && m.r_size == r_size);
int i , j ;
for (i=0; i<c_size; ++i)
for (j=0; j,r_size; i++)
p[i][j]+=m.p.[i][j];
return *this;
}
Разработка класса matrix
• inline double& matrix::operator()(int i, int j)
{
assert( i >= 0 && i < c_size && j >= 0 && j < r_size) ;
return p[i] [j] ;
}
Данная функция-член дает удобную многоаргументную нотацию для доступа к элементам. Это приводит к тому, что в клиентском коде можно использовать выражения вида m (i, j ) для доступа к элементам матрицы. Отметьте, что индексы матрицы проверяются на нахождение в пределах границ с помощью утверждений.
• matrix& matrix::operator+=(matrix& m)
{
assert(m.c_size = = c_size && m.r_size = = r_size);
Для проверки аргументов функции-члена на предусловие используется макро проверки утверждений. Присваиваемая матрица должна быть такого же размера, как и та, которой она присваивается. Этот код заменил инструкцию if-else, отвечающую за выход по ошибке. Сравните с кодом, написанным для класса vect (см. раздел 6.5, -«Класс vect», на стр. 166).
• for (i = 0; i <. c_size; ++i)
for (j = 0; j < r_size; ++j)
p[i] [j]+= m.p[i][j];
Этот вложенный цикл прозрачен и эффективен. Почленное сложение выполнено без накладных расходов.
• return (*this) ;
Возвращаемый тип является ссылкой на matrix. Разыменовывая указатель this, мы получаем lvalue объекта matrix. Это обычный прием, позволяющий выполнять множественные (порторные) присваивания.
Обсуждение данной программы продолжается в упражнении 24 на стр. 226.