Лекция
Привет, сегодня поговорим про перегрузка операции new, обещаю рассказать все что знаю. Для того чтобы лучше понимать что такое перегрузка операции new , настоятельно рекомендую прочитать все из категории С++ (C plus plus).
Операция new, заданная по умолчанию, может быть в двух формах:
1) new тип <инициализирующее выражение>
2) new тип[];
Первая форма используется не для массивов, вторая- для массивов.
Перегруженную операцию new можно определить в следующих формах, соответственно для не массивов и для массивов:
void* operator new(size_t t[,остальные аргументы] );
void* operator new[](size_t t[,остальные аргументы] );
Первый и единственный обязательный аргумент t всегда должен иметь тип size_t. Если аргумент имеет тип size_t, то в операцию-функцию new автоматически подставляется аргумент sizeof(t), т.е. она получает значение, равное размеру объекта t в байтах.
Например, пусть задана следующая функция:
void* operator new(size_t t,int n){return new char[t*n];}
и она вызывается следующим образом:
double *d=new(5)double;
Здесь t=double, n=5.
В результате после вызова значение t в теле функции будет равно sizeof(double).
При перегрузке операции new появляется несколько глобальных операций new, одна из которых определена в самом языке по умолчанию, а другие являются перегруженными. Возникает вопрос: как различить такие операции? Это делается путем изменения числа и типов их аргументов. При изменении только типов аргументов может возникнуть неоднозначность, являющаяся следствием возможных преобразований этих типов друг к другу. При возникновении такой неоднозначности следует при обращении к new задать тип явно, например:new((double)5)double;
Одна из причин, по которой перегружается операция new, состоит в стремлении придать ей дополнительную семантику, например, обеспечение диагностической информацией или устойчивости к сбоям. Кроме того класс может обеспечить более эффективную схему распределения памяти чем та, которую предоставляет система.
В соответствии со стандартом С++ в заголовочном файле <new> определены следующие функции-операции new, позволяющие передавать, наряду с обязательным первым size_t аргументом и другие.void* operatop new(size_t t)throw(bad_alloc);
void* operatop new(size_t t,void* p)throw();
void* operatop new(size_t t,const nothrow&)throw();
void* operatop new(size_t t,allocator& a);
void* operatop new[](size_t t)throw(bad_alloc);
void* operatop new[](size_t t,void* p)throw();
void* operatop new[](size_t t,const nothrow&)throw();
Эти функции используют генерацию исключений(throw) и собственный распределитель памяти(allocator).
Версия с nothrow выделяет память как обычно, но если выделение заканчивается неудачей, возвращается 0, а не генерируется bad_alloc. Об этом говорит сайт https://intellect.icu . Это позволяет нам для выделения памяти использовать стратегию обработки ошибок до генерации исключения.
1. Объекты, организованные с помощью new имеют неограниченное время жизни. Поэтому область памяти должна освобождаться оператором delete.
2. Если резервируется память для массива, то операция new возвращает указатель на первый элемент массива.
3. При резервировании памяти для массива все размерности должны быть выражены положительными величинами.
4. Массивы нельзя инициализировать.
5. Объекты классов могут организовываться с помощью операции new, если класс имеет конструктор по умолчанию.
6. Ссылки не могут организовываться с помощью операции new, так как для них не выделяется память.
Операция new самостоятельно вычисляет потребность в памяти для организуемого типа данных, поэтому первый параметр операции всегда имеет тип size_t.
Обработка ошибок операции new происходит в два этапа:
1. Устанавливается, какие предусмотрены функции для обработки ошибок. Собственные функции должны иметь тип new_handler и создаются с помощью функции set_new_handler. В файле new.h объявлены
typedef void(*new_handler)();
new_handler set_new_handler(new_handler new_p);
2. Вызывается соответствующая new_handler функция. Эта функция должна:
-либо вызвать bad_alloc исключение;
-либо закончить программу;
-либо освободить память и попытаться распределить ее заново.
Диагностический класс bad_alloc объявлен в new.h.
В реализации ВС++ включена специальная глобальная переменная _new_handler, значением которой является указатель на new_handler функцию, которая выполняется при неудачном завершении new. По умолчанию, если операция new не может выделить требуемое количество памяти, формируется исключение bad_alloc. Изначально это исключение называлось xalloc и определялось в файле except.h. Исключение xalloc продолжает использоваться во многих компиляторах. Тем не менее, оно вытесняется определенным в стандарте С++ именем bad_alloc.
Рассмотрим несколько примеров.
Пример 3.6.1
В примере использование блока try...catch дает возможность проконтролировать неудачную попытку выделения памяти.
#include <iostream>
#include <new>
void main()
{double *p;
try{
p=new double[1000];
cout<<"Память выделилась успешно"<<endl;
}
catch(bad_alloc xa)
{cout<<"Ошибка выделения памяти\n";
cout<<xa.what(); return;} }
Пример 3.6.2
Поскольку в предыдущем примере при работе в нормальных условиях ошибка выделения памяти маловероятна, в этом примере ошибка выделения памяти достигается принудительно. Процесс выделения памяти длится до тех пор, пока не произойдет ошибка.
#include <iostream>
#include <new>
void main()
{double *p;
do{
try{
p=new double[1000];
//cout<<"Память выделилась успешно"<<endl;
}
catch(bad_alloc xa)
{cout<<"Ошибка выделения памяти\n";
cout<<xa.what();
return;}
}while(p);
}
Пример 3.6.3
Демонстрируется перегруженная форма операции new-операция new(nothow).
#include <iostream>
#include <new>
void main()
{double *p;
struct nothrow noth_ob;
do{
p=new(noth_ob) double[1000];
if(!p) cout <<"Ошибка выделения памяти\n";
else cout<<"Память выделилась успешно\n";
}while(p);
}
Пример 3.6.4
Демонстрируются различные формы перегрузки операции new.
#include<iostream.h>
#include<new.h>
double *p,*q,**pp;
class demo
{ int value;
public:
demo(){value=0;}
demo(int i){value = 1;}
void* operator new(size_t t,int,int);
void* operator new(size_t t,int);
void* operator new(size_t t,char*);
};
void* demo :: operator new(size_t t,int i, int j)
{
if(j) return new(i)demo;
else return NULL;
}
void* demo :: operator new(size_t t,int i)
{demo* p= ::new demo;
(*p).value=i;
return p;
}
void* demo::operator new(size_t t,char* z)
{
return ::new(z)demo;
}
void main()
{ class demo *p_ob1,*p_ob2;
// struct nothrow noth_ob;
p=new double;
pp=new double*;
p=new double(1.2); //инициализация
q=new double[3]; //массив
p_ob1=new demo[10]; //массив объектов demo
void(**f_ptr)(int); //указатель на указатель на функцию
f_ptr=new(void(*[3])(int)); //массив указателей на функцию
char z[sizeof(demo)]; //резервируется память в соответствии с величиной //demo
p_ob2=new(z)demo; //организуется demo-объект в области памяти на //которую указывает переменная z
p_ob2=new(3)demo; //demo-объект с инициализацией
p_ob1=new(3,0)demo; //возвращает указатель NULL
// p_ob2=new(noth_ob)demo[5];//массив demo-объектов,
// в случае ошибки возвращает NULL
}
В общем, мой друг ты одолел чтение этой статьи об перегрузка операции new. Работы впереди у тебя будет много. Смело пиши комментарии, развивайся и счастье окажется в твоих руках. Надеюсь, что теперь ты понял что такое перегрузка операции new и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории С++ (C plus plus)
Из статьи мы узнали кратко, но содержательно про перегрузка операции new
Комментарии
Оставить комментарий
С++ (C plus plus)
Термины: С++ (C plus plus)