Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
недостающая теория.doc
Скачиваний:
8
Добавлен:
22.09.2019
Размер:
619.01 Кб
Скачать

5.2.8. Перегрузка операторов 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>

using namespace std;

#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

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

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]; }

int main()

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

float *ptr1,*ptr2,*ptr3;

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

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

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

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

return 0; // используя 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. Таким образом, ptr4 получает адрес массива из трех объектов класса 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>

using namespace std;

#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

}

int 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; // вызов локальной функции доопределения

return 0; // оператора 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>

using namespace std;

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; // вызов глобальной (системной) функции

}

int main()

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

delete ptr1;

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

delete ptr2;

return 0;

}

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

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

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

деструктор A

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

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

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

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

деструктор В

деструктор A

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

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