Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2014_2015 / lect5_2_2015.pptx
Скачиваний:
10
Добавлен:
27.12.2015
Размер:
258.75 Кб
Скачать

Конструктор копирования (KK) может вызываться явно, а также при определении объекта в сочетании с инициализатором:

poly a3=a1; // Синтаксис КК - определение с инициализатором poly a4(a2); // Явный вызов КК

!!! основная его задача – обеспечивать корректное копирование объектов при их передаче и возвращении в функцию (метод) по значению. Если KK def, то он автоматически вызывается компилятором еще в 2х случаях:

• при использовании формального параметра, передаваемого по значению (формальный параметр - объект, передаваемый по значению, создается в стеке в момент вызова функции и инициализируется копией фактического параметра (вызов КК));

• функция возвращает результат в виде объекта, передаваемого по значе- нию (в вызывающем контексте компилятор формирует безымянный вре- менный объект, в момент выполнения оператора return он инициализирует- ся копией объекта в выражении, записанном в операторе return (вызов КК).

!!!!!!!! деструкторы вызываются для всех объектов, в том числе и для формальных параметров – значений, и для временных объектов – результатов. Поэтому конструктор копирования «уравнивает»

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

int compare(poly &T){ // Сравнение полиномов int k=0;

poly R(*this); // Копия текущего for (int i=R.n;i>=0;i--)

R.pd[i]=-R.pd[i]; // Инвертирование коэффициентов R.add(T); // Вычитание из второго - текущего R.normalize();

if (R.n==0 && R.pd[0]==0)

return 0; // Результат - нулевой полином = равны if (R.pd[R.n]<0) return 1;

return -1;} // Иначе - знак старшего коэффициента

Переопределение операций внутри класса

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

переопределение операций в классе - возможно, если I операнд операции является объектом этого класса - вводится специальный метод:

метод определяется в классе первого операнда;имя метода – operator<знак операции>;первый операнд – текущий объект класса;

II операнд – формальный параметр, кот может быть передан по значению или по ссылке;тип формального параметра должен совпадать с типом второго операнда;

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

ограничений не накладывается (содержательная интерпретация операции может быть любой);

если формальный параметр или результат переда-ются

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

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

poly operator+(poly T){// Переопред сложения

 

T.add(*this); // II операнд по значению (копия)

 

return T; }

// Добавление I к копии II

// Переопределение сравнений

 

int operator<(poly &T) {

return compare(T)<0; }

int operator>(poly &T) {

return compare(T)>0; }

int operator==(poly &T){ return compare(T)==0;}

К экзамену – операторы !=, <=, >= и т.д.

Особенности переопределения операций

Присваивание:

 

 

·

разрушение содержимого текущего объекта –

левого операнда (аналогично деструктору);

· копирование содержимого объекта-параметра

(правого операнда) в текущий объект (аналогично

конструктору копирования);

 

·

возвращение ссылки на текущий объект.

poly &operator=(poly &R){

// Присваивание

if (&R==this) return *this;// Присваив "сам в себя" delete []pd; // Разрушить левую часть (текущий) load(R.n,R.pd);//Копия правой части (аналог констр) return *this; } // Возвращает ссылку на левый

приведение типа - особенность операции – отсутствие

формальных параметров и спецификации типа ре- зультата, поскольку он и так определяется def типом.

// переопределение приведения к int - возвращает // размерность полинома

operator int(){ return n; }

«Преобразовать» объект можно . . . к указателю. Если указатель интерпретировать как динамичес-кий массив, то можно выгрузить в него внутрен-ние данные объекта (например, к-ты полинома).

// Переопределение приведения к double* - // возвращение динамического массива operator double*(){

double *q=new double[n+2];

q[0]=n; // в нулевой ячейке – размерн полинома for (int i=0;i<=n;i++) // начиная с первой – к-ты q[i+1]=pd[i];

return q;}

Такое преобразование будет срабатывать и при присваивании объекта-полинома к указателю

(выражение вида double *q=a1;)

Переопределение операции () позволяет использовать

синтаксис вызова ф-ии применительно к объекту класса

(имя объекта с круглыми скобками). Кол-во операндов в

скобках может быть любым.

 

 

 

Переопределение операции [ ] позволяет использо-вать

синтаксис эл-та массива (имя объекта с квадр скоб).

 

В классе полиномов выражение вида a[i] позволяет

получить ссылку на i-ый к-т полинома, а выр-е вида a(i,v)

записать значение v в качестве i-го к-та

 

Операции создания и уничтожения объектов в ди-

намической памяти могут быть переопределены:

static void *operator new(size_t size);

 

 

static void operator delete (void *);

 

 

void * - указатель на область памяти, выделяемую под

объект, size

- размер объекта в

байтах, size_t - тип

размерности области памяти, int или long. Переопре-

деление этих операций позволяет написать собст-венное

распределение

памяти

для

объектов

класса.

Переопределенные операции будут вызываться при

создании динамических объектов (но не их массивов).

Переопределение операций распределения памяти class memory{

char *pa; // Обл распределяемой памяти – «кучи» int sz0;

public:При выделении памяти имеется несколько стратегий pa=newпоиска свободного блока. Простейшая - ищется своб

блок, строго подходящий по размеру. Если нашли, то он отмечается как занятый. В противном случае необходимый блок «отрезается» от конца самого первого. При таком подходе самый первый блок будет

void всегда свободен, и от него будут отрезаться

«недостающие» куски. !!Такая стратегия хороша, char когда преобладают запросы на выделение памяти

int lnt;блоками одного и того же размера.

while(p<pa+sz0){//

lnt=*(int*)p; // Извлечь из-под указателя длину блока if (lnt<0){// Занятый блок - пропустить

lnt=-lnt; // Инвертировать длину printf("busy:"); }

else printf("free:"); // Вывести 16ричный адрес и длину printf(" addr=%8x sz=%d\n",p,lnt); p+=lnt+sizeof(int); // Сдвинуть указатель на длину

// блока + длина счетчика

void *malloc(int sz) { char *p=pa;

int lnt;

while(p<pa+sz0){// Пока не достиг адреса конца области lnt=*(int*)p; // Извлечь из-под указателя длину блока if (lnt<0) // Занятый блок - пропустить

p+= Ф-я выделения памяти возвращает указатель на else {ifпамять «вслед за» счетчиком длины выделенной

области. Т. е, запоминает, сколько памяти было

выделено, и при выполнении ф-и освобождения

return

первым делом смещает указатель назад к счетчику

}длины. !! данная версия в состоянии проверить корректность возвращаемого ф-ей main адреса

} }

занятого блока (хотя и не обязана это делать).

lnt=*(int*)pa;

if (sz+sizeof(int)>lnt) return NULL; // Остаток мал –

 

// нет памяти

lnt -=sz+sizeof(int); // Уменьшить размер I блока

*(int*)pa=lnt; // и записать полученный остаток

p=pa+lnt+sizeof(int); // Указатель на новый блок

*(int*)p=-sz; // Записать в него размерность (занят – <0)

return p+sizeof(int); // Возвратить указат на память

}

// «вслед за» счетчиком

void free(void *q0) {

char *q=(char*)q0-sizeof(int); // Сместить указатель

 

// на счетчик длины

int lnt=*(int*)q; // Извлечь счетчик длины

lnt=-lnt;

char *pr=NULL,*p=pa; // pr – указат на предыдущ блок

int

Кроме формального объявления блока свободным

(путем инвертирования счетчика) необходимо еще

if

«склеить» с текущим блоком предыдущ и последующ,

pr=p;если они также свободны (дефрагментация). Для

 

поиска предыдущего блока - сделать лишний цикл

} //

просмотра содержимого распределяемой памяти с

запоминанием указателя на предыдущий блок.

*(int*)q=lnt;

if

«Cклеивание» - увеличение длины предыдущего блока

 

на размер текущего (+ счетчик длины текущего).

} p=pr;Аналогичное действие надо и для следующего блока

lnt=*(int*)p;

q=p+lnt+sizeof(int);

if (q<pa+sz0 && *(int*)q>0) // Есть следующ и он своб *(int*)p+=*(int*)q+sizeof(int); // Склеить со следующ };

}

Соседние файлы в папке 2014_2015