Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Тип_возвр_значения имя_класса::operator ()(список_аргументов)




{ тело функции}

#include "iostream.h"

Class matr

{ int **m,a,b;

public:

matr(int,int);

~matr();

int operator()(int,int); // перегрузка оператора ()

int operator()(int); // перегрузка оператора ()

};

matr::matr(int i,int j): a(i),b(j) // конструктор

{ i=0;

m=new int *[a];

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

{ *(m+k)=new int[b];

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

*(*(m+k)+n)=i++; // заполнение m числами 0,1,2,3, …a*b

}

}

matr::~matr() // деструктор

{ for(int k=0; k<a; k++)

delete [] m[k]; // освобождение памяти для k-й строки

delete [] m; // освобождение памяти для всего массива

} // указателей m

int matr::operator()(int i,int j)

{ if (i<0 || i>=a || j<0 || j>=b)

{ cerr<<"выход за пределы матрицы ";

return m[0][0]; // например, при этом возврат m[0][0]

}

return m[i][j]; // возврат требуемого элемента

}

int matr::operator()(int i)

{ if (i<0 || i>=a*b)

{ cerr<<"выход за пределы массива ";

return **m; // как и выше возврат m[0][0]

}

return m[i/b][i%b]; // возврат требуемого элемента

}

void main()

{ matr mt(3,5);

cout << mt(2,3) << endl;

cout << mt(3,2) << endl; // попытка получить элемент из 3-й строки

cout << mt(3) << endl;

}

Результаты работы программы:

выход за пределы массива 0

Конструктор класса matr динамически выделяет и инициализирует память двухмерного массива. Деструктор разрушает массив автоматически при завершении программы. В классе matr реализованы две функции operator(): первая получает два аргумента (индексы в матрице), вторая получает один аргумент (порядковый номер элемента в матрице). Обе функции возвращают либо требуемый элемент, либо элемент m[0][0] при попытке выхода за пределы матрицы.

 

Перегрузка оператора ->

Оператор -> доступа к компонентам объекта через указатель на него определяется как унарный постфиксный.

Ниже приведен простой пример программы перегрузки оператора ->:

#include "iostream.h"

#include "iomanip.h"

#include "string.h"

Class cls_A

{ char a[40];

public:

int b;

cls_A(char *aa,int bb): b(bb) // конструктор

{strcpy(a,aa);}

char *put_A() {return a;}

};

Class cls_B

{ cls_A *p; // указатель на класс cls_A

public:

cls_B(char *aa,int bb) {p=new cls_A(aa,bb);} // конструктор

~cls_B() {delete p;} // деструктор

cls_A *operator->(){return p;} // функция перегрузки ->

};

void main()

{ cls_B ptr("перегрузка оператора -> ",2); // объект класса cls_B

cout << ptr->put_A() << setw(6) << ptr->b <<endl; // перегрузка ->

cout << (ptr.operator->())->put_A() << setw(6)

<< (ptr.operator->())->b <<endl;

cout << (*ptr.operator->()).put_A() << setw(6)

<< (*ptr.operator->()).b <<endl;

}

Результат работы программы:

перегрузка оператора -> 2

перегрузка оператора -> 2

перегрузка оператора -> 2

В приведенной программе инструкции ptr->put_A() и ptr->b приводят к перегрузке операции ->, то есть позволяют получить адрес указателя на компоненты класса cls_A для (из) объекта ptr. Таким образом, инструкция ptr->b соответствует инструкции (ptr.p)->b. Следующие далее две группы инструкций также верны, но не являются примером перегрузки оператора ->, а только приводят к явному вызову функции operator-> - компоненты класса cls_B.

В целом доопределение оператора -> позволяет использовать ptr с одной стороны как специальный указатель (в примере для класса cls_A), а с другой стороны, как объект (для класса cls_B).

Специальные указатели могут быть доопредены следующим образом:

cls_A &operator*(){return *p;}

cls_A &operator[](int index){return p[index];}

а также доопределение может быть выполнено по отношению к большинству рассмотренных ранее операций (+, ++ и др.).

 

Перегрузка операторов new и delete

В С++ имеются две возможности перегрузки операторов new и delete - локально (в пределах класса) и глобально (в пределах программы). Эти операторы имеют правила переопределения, отличные от рассмотренных выше правил переопределения других операторов. Одна из причин перегрузки операторов new и delete состоит в том, чтобы придать им новые свойства, например, выдачи диагностики или более высокой защищенности от ошибок. Кроме того, может быть реализована более эффективная схема распределения памяти по сравнению со схемой, обеспечиваемой системой.

Оператор new можно задать в следующих формах:

<::> new <аргументы> имя_типа <инициализирующее_выражение>;

<::> new <аргументы> имя_типа [ ];

Параметр ”аргументы” можно использовать либо для того, чтобы различить разные версии глобальных операторов new, либо для использования их в теле функции operator. Доопределенную функцию operator new можно объявить:

void *operator new(size_t t<список _аргументов>);

void *operator new[](size_t t<список _аргументов>);

Вторая форма используется для выделения памяти для массивов. Возвращаемое значение всегда должно иметь тип void *. Единственный обязательный аргумент функции operator всегда должен иметь тип size_t. При этом в функцию operator автоматически подставляется аргумент sizeof(t).

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

#include "iostream.h"

#include "string.h"

void *operator new(size_t tip,int kol) //глобальная ф-ция operator new

{ cout << "глобальная функция 1" <<endl; // с одним параметром

return new char[tip*kol];

}

void *operator new(size_t tip,int n1,int n2) //глобальная ф-ция operator new

{ cout << "глобальная функция 2" <<endl; // с двумя параметрами

void *p=new char[tip*n1*n2];

return p;

}

Class cls

{ char a[40];

public:

cls(char *aa){ strcpy(a,aa); }

~cls(){}

void *operator new(size_t,int);

};

void *cls::operator new(size_t tp,int n) // локальная функция operator

{ cout << "локальная функция " <<endl;

return new char[tp*n]; }

void main()

{ cls obj("перегрузка оператора new");

float *ptr1;

ptr1=new (5) float; // вызов 1 глобальной функции operator new

ptr1=new (2,3) float; // вызов 2 глобальной функции operator new

ptr1=new float; // вызов сиcтемной глобальной функции

cls *ptr2=new (3) cls("aa"); // вызов локальной функции operator new

} // используя cls::cls("aa")

Результаты работы программы:

глобальная функция 1

глобальная функция 2

локальная функция

Первое обращение ptr1=new (5) float приводит к вызову глобальной функции operator с одним параметром, в результате выделяется память 5*sizeof(float) байт (это соответствует массиву из пяти элементов типа float) и адрес заносится в указатель ptr1. Второе обращение приводит к вызову функции operator с двумя параметрами. Следующая инструкция new float приводит к вызову системной функции new. Инструкция new (3) cls("aa") соответствует вызову функции operator, описанной в классе cls. В функцию в качестве имени типа передается тип созданного объекта класса cls. Таким образом, ptr2 получает адрес массива из трех объектов класса cls.

Оператор delete разрешается доопределять только по отношению к классу. В то же время можно заменить системную версию реализации оператора delete на свою.

Доопределенную функцию operator delete можно объявить:

void operator delete(void *p<,size_t t>);

void operator delete[](void *p<,size_t t>);

Функция operator должна возвращать значение void и имеет один обязательный аргумент типа void * - указатель на область памяти, которая должна быть освобождена. Ниже приведен пример программы с доопределением оператора delete.

#include "iostream.h"

#include "string.h"

#include "stdlib.h"

void *operator new(size_t tip,int kol) // глобальная функция operator

{ cout << "глобальная функция NEW" <<endl;

return new char[tip*kol]; // вызов системной функции new

}

Class cls

{ char a[40];

public:

cls(char *aa)

{ cout<<”конструктор класса cls”<<endl;

strcpy(a,aa);

}

~cls(){}

void *operator new(size_t,int);

void operator delete(void *);

};

void *cls::operator new(size_t tip,int n) // локальная функция operator

{ cout << "локальная функция " <<endl;

return new char[tip*n]; // вызов системной функции new

}

void cls::operator delete(void *p) // локальная функция operator

{ cout << "локальная функция DELETE" <<endl;

delete p; // вызов системной функции delete

}

void operator delete[](void *p) // глобальная функция operator

{ cout << "глобальная функция DELETE" <<endl;

delete p; // вызов системной функции delete

}

void main()

{ cls obj("перегрузка операторов NEW и DELETE");

float *ptr1;

ptr1=new (5) float; // вызов глобальной ф-ции доопр. оператора new

delete [] ptr1; // вызов глобальной ф-ции доопр.оператора delete

cls *ptr2=new (10) cls("aa"); // вызов локальной функции доопределения

// оператора new (из класса cls)

delete ptr2; // вызов локальной функции доопределения

// оператора delete (из класса cls)

}

Результаты работы программы:

глобальная функция NEW

глобальная функция DELETE

локальная функция NEW

конструктор класса cls

локальная функция DELETE

Инструкция cls *ptr2=new (10) cls("aa") выполняется следующим образом: вначале вызывается локальная функция operator для выделения памяти, равной 10*sizeof(cls), затем вызывается конструктор класса cls.

Необходимо отметить тот факт, что при реализации переопределения глобальной функции в ней не должен использоваться оператор delete [], так как это приведет к бесконечной рекурсии. При выполнении инструкции системный оператор delete ptr2 сначала вызывается локальная функция доопределения оператора delete для класса cls, а затем из нее глобальная функция переопределения delete.

Далее рассмотрим пример доопределения функций new и delete в одном из классов, содержащемся в некоторой иерархии классов.

#include "iostream.h"

Class A

{ public:

A(){cout<<"конструктор A"<<endl;}

virtual ~A(){cout<<"деструктор A"<<endl;} //виртуальный деструктор

void *operator new(size_t,int);

void operator delete(void *,size_t);

};

Class B: public A

{ public:

B(){cout<<"конструктор В"<<endl;}

~B(){cout<<"деструктор В"<<endl;}

};

void *A::operator new(size_t tip,int n)

{ cout << "перегрузка operator NEW" <<endl;

return new char[tip*n];

}

void A::operator delete(void *p,size_t t) //глобальная функция operator

{ cout << " перегрузка operator DELETE" <<endl;

delete p; // вызов глобальной (системной функции)

}

void main()

{ A *ptr1=new(2) A; // вызов локальной функции, используя A::A()

delete ptr1;

A *ptr2=new(3) B; // вызов локальной функции, используя B::B()

delete ptr2;

}

Результаты работы программы:

перегрузка operator NEW

конструктор A

деструктор A

перегрузка operator DELETE

перегрузка operator NEW

конструктор A

конструктор В

деструктор В

деструктор A

перегрузка operator DELETE

При public наследовании класса А классом В public функции new и delete наследуются как public функции класса В. Отметим, что необходимость использования в данном примере виртуального деструктора рассмотрена ранее.

 

Преобразование типа

Выражения, содержащиеся в функциях и используемые для вычисления не­которого значения, записываются в большинстве случаев с учетом корректности по отношению к объектам этого выражения. В то же время, если используемая операция для типов величин, участвующих в выражении, явно не определена, то компилятор пытается выполнить такое преобразование типов в два этапа.

На первом этапе выполняется попытка использовать стандартные преобразования типов. Если это невозможно, то компилятор использует преобразования, определенные пользователем.

 

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...