Перегрузка бинарных операций

Бинарная функциональная операция может определятся внутри и вне класса.

Если бинарная операция определена внутри класса, то при этом вызывающий ее объект считается первым операндом.

Пример 1

/* внутри */

Class monstr

{

bool operator>(const monstr &M)

{

if(health > M.get_health()) return true;

return false;

}

}

/* вне класса*/

bool operator>(const monstr & M1, const monstr &M2)

{

if(M1.get_health > M2.get_health()) return true;

return false;

}


 


Перегрузка операции присваивания

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

Пример 1

const monstr &operator = (const monstr &M)

{

/* проверка на самоприсвивание */

if(& == this) return *this;

if(name) delete [] name;

If(M.name)

{

name = new char[strlen(M.name)+1];

Strcpy(name, M.name)

}

else name = 0;

health = M.health;

skin = M.skin;

return *this;

}

 

Примечание: возврат из функции указателя на объект делает возможным цепочку присваиваний.

monstr A(10), B, C;

C=B=A;

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


 


Перегрузка операций new и delete

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

Использование функциональных операций new и delete должно соответствовать следующим правилам: этим функциям не требуется передавать параметр типа класса. Первым параметром функциям new и new[] должен передаваться размер объекта типа size. Этот тип, возвращаемый функцией sizeof, определяется в заголовочном файле <stddef.h>. При вызове он передается функции неявным образом. Функции new и delete должны определяться с типом возвращаемого значения void* даже если return возвращает указатель на другие типы (например, на класс). Операция delete должна иметь тип возврата void и первый аргумент типа *void. Операции new и delete являются статическими элементами класса. Поведение перегруженной операции должно соответствовать действиям, выполняемым ими по умолчанию. Для операции new это означает, что она должна возвращать правильное значение, корректно обработав запрос на выделение памяти нулевого размера или порождать исключения при невозможности выполнения запроса. Для операции delete следует соблюдать условие: удаление любого указателя должно быть безопасным, следовательно, внутри операции delete необходимо проверять указатель на NULL (при этом должно быть отсутствие каких-либо действий в случае равенства). Стандартные операции выделения и освобождения памяти могут использоваться в области действия класса наряду с перегруженными операциями. Это осуществляется с помощью операции доступа к области видимости для объектов этого класса (::) и непосредственно для всех других.

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

Пример 1

class Obj {...};

Class pObj

{

private:

obj *p;

};

PObj *p = new pObj; /* Для выделении памяти под объект типа pObj с помощью стандартной функции new */

Для небольших объемов накладные расходы могут оказать весьма значительными. Для экономии памяти можно написать собственную операцию new класса pObj. Она будет выдавать большой блок памяти и размещать в нем указатели на Obj. Для этого в объект pObj вводится статическое поле, в котором хранится указатель на первую свободную ячейку блока для размещения очередного объекта. Неиспользуемые ячейки связываются в список. Чтобы не занимать память под поле связи, можно использовать объединения (union), с помощью которых одна и та же ячейка используется либо для хранения указателя, либо для связи со следующей свободной ячейкой.


Пример 2


Class pObj

{

public:

static void *operator new (size_t size);

private:

Union

{

obj p; / * указатель на объект */

pObj next /* указатель на следующую свободную ячейку */

}

static const int BLOCK_SIZE; /* размер блока */

/* заголовок списка свободных ячеек */

static pObj *headOfFree;

};

void *pObj::operator new(size_t size)

{

/* перенаправить запрос неверного количества памяти стандартной операции new */

if(size!=sizeof(pObj)) return::operator new(size);

pObj *p = headOfFree;

/* указатель на первую свободную ячейку */

/* переместить указатель списка свободных ячеек */

if(p) headOfFree = p->next;

/* если свободной памяти нет - выделить очередной блок */

Else

pObj *new block = static_cast<pObj *>(::operator new(BLOCK_SIZE *sizeof(pObj)));

/* здесь используется явное преобразование типа с помощью операции static_cast */

/* все ячейки кроме первой свободны, связываем их */

for(int i = 1; i<BLOCK_SIZE-1; ++i)

new block[i].next = &new block[i+1];

new block[BLOCK_SIZE-1.next=0];

/* устанавливаем начало списка свободных ячеек */

headOfFree = &newblock[1];

p = newblock;

return p; /* возвращаем указатель на память */

}

/* должна присутствовать инициализация статических полей класса pObj */

pObj*pObj::headOfFree;

/* устанавливаются нули по умолчанию */

const int pObj::BLOCK_SIZE = 1024;

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

void pObj::operator delete(void * ObjToDie, size_t size)

{

if(ObjToDie == 0) return;

if(size!=sizeof(pObj))

{

::operator delete(ObjToDit);

return;

}

pObj p = static_cast<pObj>(ObjToDie);

p->next=headOfFree;

headOfFree=p;

}

 

Если перегруженная операция new наследуется, то она вызывается для производных объектов. Причем если их размер не соответствует размеру базового объекта (это встречается часто), то это приводит к проблемам. Для избежания проблем необходимо осуществлять проверку на размер. Если размер объекта не равен тому, для которого перегружалась new, то запрос на выделение памяти следует осуществлять с помощью не перегруженной new.

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


 



Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: