русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Лабораторная работа №3


Дата добавления: 2015-07-09; просмотров: 824; Нарушение авторских прав


Тема: “Перегрузка операций”

Перегрузка операторов позволяет в выражениях применять к объектам класса обычные операции, такие как сложение или вычитание.

Перегруженными могут быть практически все операторы, кроме операторов разрешения области видимости, выбора элемента класса, структуры или объединения, условного.

Перегрузка операторов напоминает перегрузку функций и поэтому называется операторной функцией. Однако при этом вводятся некоторые дополнительные правила:

Ø Так как перегружаемый оператор всегда связан с классом, то нельзя переопределять операторы для встроенных типов и нельзя определять дополнительные операторы для встроенных типов;

Ø Нельзя изменять предопределенный приоритет операторов;

Ø Нельзя изменять «-арность» операций, то есть нельзя бинарную операцию сделать унарной или наоборот унарную – бинарной;

Ø Операторная функция может быть перегружена, как и обыкновенная функция с соблюдением правил перегрузки.

Ø Нельзя создавать новых операторов кроме тех, которые определены стандартом С++.

Ø Для операторной функции нельзя задавать стандартные значения параметров.

В общем случае операторная функция объявляется следующим образом:

type operator знак_оператора(список_аргументов);

 

Часто возвращаемым значением операторной функции является класс, для которого она задана. Однако операторная функция может возвращать любой тип.

Когда операторная функция – член класса перегружает бинарный оператор, для функции достаточно только одного параметра. Этот параметр получит объект, расположенный справа от знака оператора. Объект слева вызывает операторную функцию и передается неявно, посредством использования указателя this.

При создании класса некоторые функции компилятор «подсовывает» по умолчанию. И если Вас не удовлетворяют такие функции, не поленитесь перегрузить их. К таким функциям относится функция присваивания.



Перегрузка унарных операторов напоминает перегрузку бинарных, за исключением того, что мы имеем дело только с одним операндом. Если при этом операторная функция является функцией-членом класса, то у нее не будет параметров. Поскольку имеется только один операнд, он и вызывает операторную функцию.

Иногда нужно объявить два класса, которые так тесно связаны друг с другом концептуально, что хотелось бы обеспечить одному из них неограниченный доступ к членам второго. Проблемы подобного рода в язык С++ решаются через дружественный класс, который имеет доступ как к общим элементам, так и к частным и защищенным элементам класса. Для того, чтобы объявить весь класс или только какую-либо функцию дружественной какому-либо классу используется ключевое слово friend. На еще необъявленный класс можно ссылаться, как на дружественный.

Для перегрузки операторов можно использовать не только функции-члены, но и дружественные функции. При этом необходимо помнить, что в дружественную функцию не передается указатель this. Поэтому в случае бинарного оператора это означает, что дружественной операторной функции явно передаются два параметра, а для унарных операторов требуется один операнд. Все остальное в обоих случаях одинаково. И естественно, при определении дружественной операторной функции вне тела класса не требуется определять область видимости к создаваемому классу.

Перегрузка оператора посредством дружественной функции дает одну очень важную возможность, которой нет у функции – члена класса. Используя дружественную операторную функцию, в операциях с объектами можно использовать встроенные типы данных, которые могут располагаться слева от оператора, что запрещено для функций-членов класса.

Такие свойства дружественной функции делает ее незаменимой для перегрузки операторов ввода/вывода, поскольку в таких операторах левым операндом является объект одного из поточных классов.

Пример 1: Класс, описывающий окружность.

#include <iostream>

#include <conio.h>

#include <windows.h>

#define _USE_MATH_DEFINES

#include <cmath>

using namespace std;

 

class Circle

{

// Элементы данных:

float x, y, r;

/* дружественные функции ввода и вывода (эти функции должны быть именно дружественными, поскольку левым операндом операции является ссылка на поток, а не окружность): */

friend istream& operator >> (istream&, Circle&);

friend ostream& operator << (ostream&, const Circle&);

//функции – члены класса:

public:

//конструкторы:

// два в одном: основной конструктор и конструктор по умолчанию

Circle(float xx=0, float yy=0, float rr=5):x(xx),y(yy),r(rr) {}

Circle(const Circle &); //конструктор копирования

//деструктор:

~Circle(){}

// функции доступа:

void Set_x(float xx) {x=xx;}

void Set_y(float yy) {y=yy;}

void Set_r(float rr) {r=rr;}

float Get_x() {return x;}

float Get_y() {return y;}

float Get_r() {return r;}

// функции – члены класса, обеспечивающие ввод-вывод:

void input();

void output();

// перегрузка операций:

// операция присваивания = :

Circle& operator = (const Circle &);

//операция + :

Circle operator + (const Circle &);

// операция += :

Circle& operator += (const Circle &);

//прочие функции:

float Area() {return M_PI*r*r;}

float Line() {return 2*M_PI*r;}

float Mass_Center(int, const Circle*);

};

// Определение функций:

 

// перегрузка оператора присваивания:

Circle& Circle::operator = (const Circle &obj)

{

if (this != &obj) // проверка на самоприсваивание (типа с1 = с1;)

{ /* в данном случае просто элементы данных того объекта, который будет стоять слева от знака присваивания (this), становятся равными элементам данных объекта, который будет стоять справа от знака присваивания (obj) */

x=obj.x; y=obj.y; r=obj.r;

}

return *this; /* функция возвращает текущий объект, это позволит выполнять множественное присваивание типа c1 = c2 = c3 */

}

 

/* перегрузка операции сложения окружностей */

Circle Circle::operator + (const Circle &obj)

{

Circle tmp;

tmp.x = x + obj.x;

tmp.y = y + obj.y;

tmp.r = r + obj.r;

return tmp; /* функция возвращает временный объект, который будет существовать в памяти только во время выполнения операции*/

}

 

/* перегрузка операции сложения c присваиванием (эта функция позволит изменять параметры окружности, увеличивая или уменьшая их, одной операцией): */

Circle& Circle::operator += (const Circle &obj)

{

x += obj.x;

y += obj.y;

r += obj.r;

return *this; //функция возвращает текущий объект

}

/* функция, которая вычисляет усредненное расстояние от заданной точки (центра текущей окружности) до множестваточек (центров других окружностей), так называемый “центр масс” окружностей, по формуле:

*/

float Circle::Mass_Center(int n, const Circle* obj)

//параметрами функции являются количество точек и массив точек

{

float sum = 0;

for (int i=0; i<n; i++)

sum+=sqrt((x + obj[i].x)*(x + obj[i].x)+ (y + obj[i].y)*(y + obj[i].y));

sum/=n;

return sum;

}

 

// Определение дружественных функций ввода-вывода:

istream& operator >> (istream& is, Circle& obj)

{

cout << "Задайте x:" << endl;

is >> obj.x;

cout << "Задайте y:" << endl;

is >> obj.y;

do

{

cout << "Задайте r:" << endl;

is >> obj.r;

}while(obj.r<0);

return is;

}

 

ostream& operator << (ostream& os, const Circle& obj)

{

os << "Значение x:" << obj.x << endl;

os << "Значение y:" << obj.y << endl;

os << "Значение r:" << obj.r << endl;

return os;

}

 

// Конструктор копирования:

Circle::Circle(const Circle& obj)

{

x=obj.x; y=obj.y; r=obj.r;

}

 

// Функции-члены класса для ввода и вывода:

void Circle::input()

{

cout << "Задайте x:" << endl;

cin >> x;

cout << "Задайте y:" << endl;

cin >> y;

cout << "Задайте r:" << endl;

cin >> r;

}

 

void Circle::output()

{

cout << "Значение x:" << x << endl;

cout << "Значение y:" << y << endl;

cout << "Значение r:" << r << endl;

}

 

// пример программы, которая использует класс Circle:

int main()

{

//Настройки шрифтов и региональных стандартов:

if(SetConsoleCP(1251)==0)

//проверка правильности установки кодировки символов для ввода

{

cerr<<"Fialed to set codepage!"<<endl;

/* если не удалось установить кодовую страницу, вывод сообщения об ошибке */

}

if(SetConsoleOutputCP(1251)==0)//тоже самое для вывода

{

cerr<<"Failed to set OUTPUT page!"<<endl;

}

Circle a;

//пример использования основного конструктора:

a.output();

Circle b(5.6, 7.2, 3.7);

b.output();

// пример использования конструктора копирования:

Circle c(b);

c.output();

//пример использования операции присваивания:

b = a;

b.output();

// пример использования дружественных функций ввода-вывода:

cin >> c;

cout << c;

// пример использования функции, вычисляющей “Центр масс” окружностей:

int k=3;

Circle* CM = new Circle[k]; // массив окружностей

for (int i=0; i<k; i++)

cin >> CM[i];

cout << "Центр масс окружностей:" << c.Mass_Center(k,CM)<< endl;

delete [] CM;

// пример применения операции сложения окружностей:

c = a + b;

cout << c;

c += a;

cout << c;

// пример применения функций доступа:

a.input();

a.output();

float xx,yy,rr;

cout << "Задайте значение x:" << endl;

cin >> xx;

cout << "Задайте значение y:" << endl;

cin >> yy;

cout << "Задайте значение r:" << endl;

cin >> rr;

// Изменяем параметры окружностей:

a.Set_x(xx);

a.Set_y(yy);

a.Set_r(rr);

cout << "Значение x=" << a.Get_x() << endl;

cout << "Значение y=" << a.Get_y() << endl;

cout << "Значение r=" << a.Get_r() << endl;

a.output(); // Пример использования вычислительных функций:

cout << "Площадь окружности :" << a.Area() << endl;

cout << "Длина окружности :" << a.Line() << endl;

_getch();

return 0;

}

Пример 2: Класс, описывающий прямоугольник.

 

#include <iostream>

#include <conio.h>

#include <windows.h>

using namespace std;

 

class CSize

{

friend class CRect;

/* Объявляем класс CRect дружественным для того, чтобы он имел доступ к элементам данных класса CSize */

int cx, cy; // размер по горизонтали и размер по вертикали

public:

CSize(int x = 0, int y=0): cx(x), cy(y){};

// в данной программе требуется только конструктор класса CSize

};

 

// Класс, описывающий прямоугольники:

class CRect

{

int left, top, right, bottom;

// координаты верхнего левого и нижнего правого углов прямоугольников

public:

// Конструктор:

CRect (int l=0, int t=0, int r=0, int b = 0)

{ left = l; top = t; right = r; bottom = b;}

// Ширина и высота прямоугольника:

int Width() { return right - left;}

int Height(){ return bottom - top;}

// функция, которая выводит координаты левого верхнего и правого нижнего
// углов прямоугольника:

void Draw()

{

cout << "(" << left << ";" << top << ") (" << right << ";" << bottom << ")" << endl;

}

// операция +=, которая выполняет сдвиг прямоугольника на заданные расстоя­// ния:

CRect& operator += (CSize& size)

{

left += size.cx;

right += size.cx;

top += size.cy;

bottom += size.cy;

return *this; //функция возвращает текущий объект

}

// операция +, которая выполняет сдвиг прямоугольника на заданные расстояния:

CRect operator + (CSize& size)

{

CRect tmp = *this;

tmp += size;

return tmp; // функция возвращает временный объект

}

};

 

//===================================================

int main()

{

//Настройки шрифтов и региональных стандартов:

if(SetConsoleCP(1251)==0)

//проверка правильности установки кодировки символов для ввода

{

cerr<<"Fialed to set codepage!"<<endl;

/* если не удалось установить кодовую страницу, вывод сообщения об ошибке */

}

if(SetConsoleOutputCP(1251)==0)//тоже самое для вывода

{

cerr<<"Failed to set OUTPUT page!"<<endl;

}

CRect rect0(10,10, 50,30); // Объявляем прямоугольник

CRect rect = rect0;

// копия прямоугольника (соответствующую операцию сгенерирует компилятор)

// выводим информацию о прямоугольниках:

for (int i=0; i<=11; i++) // счетчик i отсчитывает ряды прямоугольников

{

for (int j=1; j<=14; j++)

// в каждом ряду расположено по 14 прямоугольников

{

rect.Draw(); // “рисуем” прямоугольник

rect += CSize(rect.Width()+2, 0);

// сдвигаем прямоугольник вправо, используя операцию +=

}

rect = rect0 + CSize(0, (rect.Height()+5)*i);

// сдвигаем прямоугольник вниз с помощью операции +

}

_getch();

return 0;

}

Пример 3: Класс, описывающий строку символов

В этом примере описание класса выделено в отдельный файл.

 

// файл _string_.h:

 

#include <string.h>

#include <iostream>

#include <assert.h>

using namespace std;

 

class String

{

char* str; // указатель на строку

int len; // длина строки

// операторные функции ввода-вывода строки:

friend istream& operator >> (istream&, String&);

friend ostream& operator << (ostream&, String&);

 

public:

String() //Конструктор по умолчанию

{ str = NULL; len=0;}

String(char *); //Основной конструктор

String(const String &); //Конструктор копирования

int GetLength() {return strlen(str);} // количество символов строки

int GetMem() {return len;} // объем памяти, выделенный для символов // строки

~String(){ delete [] str;} // деструктор

// операторные функции:

String& operator += (const String&);

String operator + (const String&);

String& operator = (const String&);

char& operator [](int i);

String operator () (int, int); //Подстрока

};

 

//Определение функций:

 

// функция ввода:

istream& operator >> (istream& is, String& st)

{

char *tmp = new char[129];

/* используем временную строку, поскольку заранее неизвестно, сколько символов будет введено */

is.getline(tmp, 128);

st = String(tmp);

delete []tmp;

return is;

}

 

// функция вывода:

ostream& operator << (ostream& os, String& st)

{

os << st.str;

return os;

}

 

// основной конструктор:

String::String(char* source)

{

len = strlen(source);

str = new char[len+1];

strcpy(str, source);

}

 

// конструктор копирования:

String::String(const String& source)

{

len = source.len;

str = new char[len + 1]; /* конструктор создает новый объект, поэтому освобождение ранее выделенной памяти не требуется */

strcpy(str, source.str);

}

 

// оператор присваивания:

String& String::operator = (const String& source)

{

if (&source != this) //Проверка самоприсваивания

{

len = source.len;

if (str) delete []str; // освобождаем ранее выделенную память

str = new char[len+1];

strcpy(str,source.str);

}

return *this; // возвращаем текущий объект

}

 

// операция += (добавляет строку к текущему объекту):

String& String::operator += (const String& source)

{

len = len + source.len;

char* tmp = new char[len+1]; // выделяем память для суммы строк

strcpy(tmp, str); // копируем символы текущего объекта

strcat(tmp, source.str); // добавляем символы второй строки

if (str) delete [] str; // освобождаем память, ранее выделенную текущему объекту

str = tmp; // новая строка становится основой объекта

return *this; // функция возвращает текущий объект

}

 

// операция + :

String String:: operator + (const String& str2)

{

String str1(str); // создаем временный объект, чтобы не изменять текущий

str1 += str2; // используем уже запрограммированную операцию +=

return str1; // возвращаем временный объект

}

 

/*операция [], которая позволяет считывать и изменять символы строки, используя номер символа: */

char& String::operator [](int i)

/*тип функции обязательно должен быть ссылкой, иначе невозможно будет изменять символы */

{

assert(i>=0 && i<len); /*функция assert() проверяет условие и, если оно не выполняется, выводит сообщение об ошибке и останавливает работу программы */

return str[i];

}

 

/*операция (), которая будет выбирать из строки подстроку, начиная с символа с заданным номером index; длина подстроки определяется параметром count */

String String:: operator () (int index, int count)

{

String sub;

if (index <= len-1) // проверяем, не выходит ли индекс за границы строки

{

int ost = len-index;

// определяем, сколько символов осталось до конца строки

sub.len = count>ost ? ost: count;

/* если запрошенное количество символов больше, чем имеется, то функция вернет имеющийся остаток строки */

sub.str = new char[sub.len+1]; // выделяем память

strncpy(sub.str, str+index, count); // копируем сиволы

sub.str[sub.len]='\0';

// функция strncpy() не дописывает признак конца строки

}

return sub;

}

// файл string.cpp:

#include <iostream>

#include <conio.h>

#include <windows.h>

#include "_string_.h"

using namespace std;

 

 

int main()

{

//Настройки шрифтов и региональных стандартов:

if(SetConsoleCP(1251)==0)

//проверка правильности установки кодировки символов для ввода

{

cerr<<"Fialed to set codepage!"<<endl;

/* если не удалось установить кодовую страницу, вывод сообщения об ошибке */

}

if(SetConsoleOutputCP(1251)==0)//тоже самое для вывода

{

cerr<<"Failed to set OUTPUT page!"<<endl;

}

do

{

String s1, s2; // исходная и результирующая строки

cout<<"Введите строку: ";

cin>>s1;

int len=s1.GetLength();

if (len)

{

unsigned n;

cout << "На сколько символов сдвинуть строку? ";

cin>>n;

n %= len; /* в результате циклического сдвига на длину строки, она принимает исходное состояние, поэтому имеет смысл выполнять сдвиг только на число, равное остатку от деления n на длину строки */

cout << "Результат:\n";

s2 = s1(n, len-n) + s1(0,n);

// в результате сдвига влево первые n символов уходят в конец строки

cout <<s2<<endl;

/* проверим соответствие выделяемой под строку памяти и количества символов строки (они должны быть равны): */

cout << "длина " << s2.GetLength() <<

" память: " << s2.GetMem() << endl;

}

} while (_getch() != 27);

return 0;

}



<== предыдущая лекция | следующая лекция ==>
Задания для самостоятельного выполнения | Задания для самостоятельного выполнения


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 1.09 сек.