Помогни ни да направим Uroci.net по - богат! Добави урок

C++ част.5 (Свободна памет)

C++ » C++
fix3d   трудност:    видян: 24130



Четирикратното изпълнение на exhaustFreeStore() С аргументи, които имат различен размер дава следния резултат

Free Store Exhaused
ckunk 1000000 depth 4

Free Store Exhaused
ckunk 100000 depth 22

Free Store Exhaused
ckunk 10000 depth 209

Free Store Exhaused
ckunk 1000 depth 2072

Една от С++ библиотеките предлага известна помощ, като поддържа информация за свободната памет. Манипулаторът, обработващ изключенията _new_handler се разглежда в Раздел 5.4 по-нататък в тази глава.

Програмистът също може да постави обект, разположен в свободната памет на определен адрес. Формата на такова извикване на оператора new има вида

new (place_address) type-specifier

където place_address трябва да бъде указател. За да използвате оператора new по този начин трябва включите заглавния файл new.h. По този начин програмистът може да преразпределя паметта, която в един по-нататъшен момент ще съдържа обекти, определени чрез тази форма на оператора new. Например,

#include <stream.h>
#include <new.h>

const Chunk = 16;

class Foo { public
int val;
Foo() { val = 0; }
};// preallocate memory, but no Foo objects

char *buf = new char[ sizeof( Foo ) * Chunk ];
main() {
// construct Chunk Foo objects for buf
Foo *pb = new (buf) Foo[ Chunk ];

// check that objects were plased in buf
if ( (char*)pb == buf )
cout << "Operator new worked! pb "<< pb << " buf "
<< (void* )buf << "n";
}

Когато тази програма бъде компилирана и изпълнена ще получим следните резултати

Operator new worked!
pb 0x234cc
buf 0x234cc

Възможно е да се появи известно объркване относно тази програма. То е свързано с изпращането на buf към void*. Това е необходимо, понеже когато към оператора за изход се изпраща операнд char* се отпечатва “null terminated string”, т.е. низа, който е адресиран. Чрез изпращането на buf към void* операторът за изход знае, че трябва да отпечата адресната стойност на buf. Това се дължи на факта, че операторът за изход се презарежда така, че да използват два различни указателни типове на аргументи char* и void*. Презаредимите функции се разглеждат в един от подразделите на тази глава. Въпреки, че този тип на оператора new се използва главно с типовете class, той може да се използват и за вградените типове данни. Например,

#include <new.h>

int *pi = new int;

main(){
int *pi2 = new (pi) int;
}


5.2. Един пример за свързан списък

В този раздел се реализира един елементарен клас списък от цели числа за да бъде илюстрирана както работата с указатели, така и използването на операторите new и delete. Като минимум IntList трябва да поддържа две стойности - цялата стойност на елемента на списъка и адреса на следващия елемент на списъка. Това може да се представи по следния начин

int val;
ListItem *next;

Един списък представлява последователност от елементи. Всеки елемент съдържа стойност и указател, може и null, към следващия елемент на списъка. Списъкът може да бъде и празен; т.е. да бъде списък без елементи

IntList i1; // the empty list

Списъкът може да нараства чрез добавяне на елементи. Тези елементи могат да бъдат вмъквани в началото на списъка

i1.insert( someValue );

или добавяни към края му

i1.append( someValue );

Списъкът може да бъде намаляван чрез отстраняване на елементи (предполага се, че той не е празен)

i1.remove( someValue );

Потребителят трябва да бъде в състояние да показва елементите на на списъка

i1.display();

Ето една първа програма, която бихме желали да напишем като използваме класа IntList.

#include "IntList.h"

const SZ = 12;

main() {
IntList i1;
i1.display();
for ( int i = 0; i < SZ; ++i )
i1.insert( i );
i1.display();
IntList i12;
for ( i = 0; i <SZ; ++i ) i12.append( i );
i12.display();
return 0;
}

Когато тази програма бъде компилирана и изпълнена се получава следния резултат

( empty )( 11 10 9 8 7 6 5 4 3 2 1 0 )( 0 1 2 3 4 5 6 7 8 9 10 11 )

Първата стъпка за реализацията на тази програма е дефинирането на класа IntList. Това е и първото място, където можем да сгрешим. Неправилен за проекта избор ще бъде да декларираме както val, така и next като членове на IntList. Например,

class IntList {
public IntList ( int = ??? );
// ...private
int val;
IntLIst *next;
};

При този проект възникват няколко проблема. Всичките те произтичат от объркването между обекта списък и елемента на списъка. Например, при този проект не се допуска наличието на празен списък. Не съществува начин за разграничаване на списъка, съдържащ един елемент от празния списък. Въпросителните знаци, в сигнатурата на конструктора на IntList са предназначени да подчертаят този проблем. Няма подразбираща се стойност за инициализиране на val, чрез която да се отбелязва, че списъкът е празен. Другите проблеми възникват от това, че не е определен смисъла на insert() и remove() когато обектът от тип IntList предлага също и първия елемент на списъка.

В пректа на IntList трябва да бъде направено разграничаване между елементите на списъка и самия обект списък като такъв. Един от начините да бъде направено това е да бъде дефиниран както клас IntList, така и клас IntItem. Ето дефиницията на IntItem

class IntList;
class intItem{
friend class IntList;
private IntItem( int v=0 ) { val = v; next = 0 )
IntItem *next;
int val;
};

IntItem се нарича клас private (личен). Само на IntList е разрешено да създава и обработва IntItem обектите. Това е смисъла на декларацията friend. Раздел 6.5 разглежда подробно тази декларация. Раздел 6.1 обяснява разликите между декларациите private и public. IntList е реализиран по следния начин:

class IntItem;
class IntList {
public IntList(int val) { list = new IntItem( val ); }

IntList() { list = 0; }// ...
private
IntItem *list;
};

Упражнение 5-1. Защо IntList се нуждае от два конструктора? Защо, например, да не дефинираме простоIntList( val = 0 );
Упражнение 5-2. Един допълнителен член данни на IntList може да бъде int len; // length of list, който да съдържа броя на елементите на списъка. Разгледайте аргументите за и против тази декларация.

Следващата стъпка се състои в реализирането на член-функции, които поддържат потребителските обработки на IntList обектите. insert() поставя даден нов IntItem в началото на списъка. Това се реализира така

IntList
insert( int val ) {
// add to the front of the list
IntItem *pt = new IntItem( val );
pt->next = list;
list = pt;
return val;
}

append() е малко по-сложна. Тя трябва да добавя нов IntItem в края на списъка. Една помощна функция, atEnd(), връща указател към последния елемент на списъка

IntItem *IntList
atEnd(){ // return pointer to last item on list
IntItem *prv, *pt;
for ( prv=pt=list; pt; prv=pt; pt=pt->next ); // null statement
return prv;
}

append() трябва да проверява специалния случай на празен списък. Реализацията изглежда по следния начин
IntList
append( int val ) {
// add to the back of the list
IntItem *pt = new IntItem( val );
if ( list == 0 ) list = pt;
else (atEnd())->next = pt;
return val;
}


Страници: «1 2 3 4 5 »

Сподели урока:



Регистрирайте се, за да добавите коментар


Калдейта Ком ЕООД - © 2003-. Всички права запазени.
Препоръчваме: IT Новини