Дополнительные возможности использования функций
Дополнительные возможности позволяют:
- Перегружать функции-члены:
- Перегружать операторы.
- Создавать функции для поддержания классов с динамическим выделением памяти для переменных.
Мы уже говорили о перегрузке функций на занятии посвящённом функциям. Функции-члены можно перегрузить точно так же.
#include<iostream.h>
class Rect
{ public:
Rect(int width,int height);
~Rect() {}
void DrawRect(int aWidth,int aHeight);
void DrawRect();
private:
int itsWedth;
int itsHeight;
};
Rect::Rect(int wedth,int height)
{
itsWedth=wedth;
itsHeight=height;
}
void Rect::DrawRect()
{
DrawRect(itsWedth,itsHeight);
}
void Rect::DrawRect(int aWedth,int aHeight)
{
for(int i=0;i<aHeight;i++)
{
for(int j=0; j<aWedth;j++)
cout<<"*";
cout << "\n";
}
}
int main(void)
{
Rect theRect(30,5);
cout <<"Печать того, что уже есть\n";
theRect.DrawRect();
cout <<"Печать нового\n";
theRect.DrawRect(40,10);
return 0;
}
Таким же образом можно перегружать конструкторы.
В конструкторе можно инициализировать члены не в теле, а в момент инициализации самого конструктора, синтаксис выглядит следующим образом:
Rect::Rect(int wedth,int height):
ItsWedth(wedth),
ItsHeight(height) {}
Особенностью функции, которая имеет параметр по умолчанию является возможность ситуации: если параметр не указывается в скобках при вызове, то он имеет это значение. Значения параметров по умолчанию могут задаваться справа налево, пример: void DrawRect (int aWidth, bool Use = false, int aHeight);не может быть использован, так как самый правый параметр по умолчанию не имеет значения.
Выбор между перегруженными функциями и значениями по умолчанию с одной стороны является делом каждого программиста, но существуют общие принципы, когда перегрузка функции предпочтительнее:
- Не существует стандартных общепринятых значений, которые можно было бы использовать по умолчанию;
- В программе в зависимости от ситуации необходимо использовать разные алгоритмы;
- Необходимо иметь возможность изменять тип значений, передаваемых в функцию.
Как уже говорилось, существует конструктор по умолчанию, который никак себя не проявляет, пользователь имеет возможность написать свой собственный конструктор по умолчанию ( по определению это конструктор не имеющий параметров). Это необходимо если в программе был создан свой конструктор, с этого момента компилятор перестаёт предлагать конструктор по умолчанию, даже в ситуации, когда необходим конструктор без параметров.
Конструктор может быть перегружен, как и любая другая функция, это необходимо для повышения гибкости программы. Например: можно в предыдущем примере создать конструктор, который при введении его без параметров будет использовать значения по умолчанию.
Функции — друзья
Дружественная функция — это функция, которая не является методом класса, но имеет доступ к собственным и защищённым элементам класса. Функция не может быть другом без согласия класса, то есть она должна быть описана в теле класса с помощью специального слова friend.
Дружественная функция при вызове не получает указателя *this Объекты классов должны передаваться явно через аппарат параметров. При её вызове нельзя использовать аппарат вызова метода класса. Использование дружественных функций позволяет упростить интерфейс между классами. Можно сделать все функции класса Y друзьями класса X:
Class Y
{
void f1(X&);
void f2(X*);
};
class X
{
friend Y;
int I;
void f3();
}
Перегрузка операторов
Форма для перегрузки операторов:
Объявление перегруженных операторов осуществляется так же, как и функций. Используется специальное слово: operator , за которым следует сам перегружаемый оператор. В функциях с одним операндом параметры не задаются
Const класс & класс::operator …();
Перегрузка оператора с двумя операндами требует наличия параметров (константные ссылки на объекты этих классов):
Класс класс::operator + (const класс &rhs) — заголовок и след. Описание
Класс operator + (const класс &)
Рекомендации:
- перегружайте операторы. Если код программы после этого станет чётче и понятнее.
- Возвращайте объекты класса из перегруженных операторов.
- Не увлекайтесь созданием перегруженных операторов, выполняющих несвойственные им функции.
#include<iostream.h>
class V_3d
{
double x,y,z;
public:
V_3d(const V_3d &v1); // конструктор -копировщик
V_3d(double x1=0,double y1=0,double z1=0):
// конструктор со значениями по умолчанию
x(x1),y(y1),z(z1) {}; // инициация переменных-членов
void print(); // печать объекта класса
V_3d operator +(V_3d t); // перегрузка оператора +
V_3d operator =(V_3d t); // перегрузка оператора =
friend ostream &operator <<(ostream &stream,V_3d t);
friend istream &operator >>(istream &stream,V_3d &t);
double mod(); // функция модуля
};
V_3d:: V_3d(const V_3d &v1)
{ x=v1.x;
y=v1.y;
z=v1.z;
}
void V_3d::print()
{
cout<<"x="<<x<<" y="<<y<<" z="<<z<<"\n";
}
V_3d V_3d:: operator +(V_3d t)
{
V_3d c;
c.x=x+t.x; c.y=y+t.y; c.z=z+t.z;
return c;
}
V_3d V_3d:: operator =(V_3d t)
{
x=t.x; y=t.y; z=t.z;
return *this;
}
ostream &operator <<(ostream &stream,V_3d t)
{
stream<<"x="<<t.x<<",";
stream<<"y="<<t.y<<",";
stream<<"z="<<t.z<<"\n";
return stream;
}
istream &operator >>(istream &stream,V_3d &t)
{
stream >>t.x>>t.y>>t.z;
return stream;
}
double V_3d:: mod()
{
return x*x+y*y+z*z;
}
void main(void)
{
V_3d a;
V_3d b(4,7,10);
V_3d c(b);
cout << "координаты вектора b: \n";
cout <<b;
cout << "координаты вектора c: \n";
c.print();
cout <<"введите координаты вектора a:\n";
cin >>a;
cout << "координаты вектора а: \n";
cout <<a;
c=a+b;
cout << "координаты вектора a+b \n";
c.print();
}
Результат работы программы:
координаты вектора b:
x=4,y=7,z=10
координаты вектора c:
x=4 y=7 z=10
введите координаты вектора a:
-1 3 6
координаты вектора а:
x=-1,y=3,z=6
координаты вектора a+b
x=3 y=10 z=16
Конструктор-копировщик
В предложенной программе используются дополнительные возможности функций-членов — перегрузка, использование значений по умолчанию, использование нескольких конструкторов, в том числе и по умолчанию, перегрузка операторов = и +и конструктор –копировщик Копировщик, предоставляемый компилятором, осуществляет схему поверхностного копирования может привести к ошибкам в случае, если среди данных-членов класса имеется указатели.
Вторая схема представляет поверхностное копирование (если один из компонентов класса содержит указатель) и в случае уничтожения этого данного, уничтожится значение и для объекта, который был скопирован. (так как существует два указателя указывающие на один адрес)
Первая схема для глубинного копирования позволяет полностью скопировать все данные, а не только указатели.