C++: библиотека программиста



Download 1,95 Mb.
Pdf ko'rish
bet109/144
Sana24.02.2022
Hajmi1,95 Mb.
#223123
TuriРеферат
1   ...   105   106   107   108   109   110   111   112   ...   144
Bog'liq
C -Eldjer-Djeff-for-Real-Programmers-RUS-www.itlibitum.ru

 
195 
class Foo { 
public: 
void* operator new(size_t bytes) 

if (bytes != sizeof(Foo) || fgFreeList == NULL) 
return 
::operator 
new(bytes); 
FreeNode* node = fgFreeList; 
FgFreeList 

fgFreeList->next; 
Return 
node; 

}; 
Мы избавились лишь от проблем, связанных с выделением памяти. Процесс освобождения необходимо 
изменить в соответствии с этой стратегией. Альтернативная форма оператора 
delete
имеет второй 
аргумент — количество освобождаемых байт. На первый взгляд кажется, что из затруднений появился 
изящный выход: 
class Foo { 
public: 
void* operator new(size_t bytes); 
// См. Выше 
void operator delete(void* space, size_t bytes) 

if (bytes != sizeof(Foo)) 
::operator 
delete(space); 
((FreeNode*)space)->next = fgFreeList; 
fgFreeList 

(FreeNode*)space; 

}; 
Теперь в список будут заноситься только настоящие 
Foo
и производные классы, совпадающие по 
размеру. Неплохо, но есть одна проблема. Как компилятор поведет себя в следующем фрагменте? 
Foo* foo = new Bar; 
delete foo; 
// Какой размер будет использован компилятором? 
Bar
больше 
Foo
, поэтому 
Foo::operator new
перепоручает работу глобальному оператору 
new
. Но 
когда подходит время освобождать память, компилятор все путает. Размер, передаваемый 
Foo::operator delete
, основан на догадке компилятора относительно настоящего типа, а эта 
догадка может оказаться неверной. В данном случае мы сказали компилятору, что это 
Foo
, а не 
Bar

компилятор ухмыляется и продолжает играть по нашим правилам. Чтобы справиться с затруднениями, 
необходимо знать точную последовательность уничтожения, возникающую в операторах вида 
delete 
foo;
. Сначала вызываются деструкторы, начиная с производного класса, и далее вверх по цепочке. 
Затем оператор 
delete
вызывается кодом, окружающим деструктор производного класса. Это 
означает, что проблема возникает только для невиртуальных деструкторов. Если деструктор является 
виртуальным, аргумент размера в операторе 
delete
всегда будет правильным — 2438-й довод в 
пользу применения виртуальных деструкторов, если только у вас не находится действительно веских 
причин против них. 
Рабочий класс списка свободной памяти 
Учитывая все сказанное, следующий фрагмент всегда будет правильно работать на компиляторах, 
использующих v-таблицы. 
class Foo { 
private: 
struct 
FreeNode 

FreeNode* 
next; 
}; 


 196 
static FreeNode* fdFreeList; 
public: 
virtual ~Foo() {} 
void* operator new(size_t bytes) 

if (bytes != sizeof(Foo) || fgFreeList == NULL) 
return 
::operator 
new(bytes); 
FreeNode* node = fgFreeList; 
FgFreeList 

fgFreeList->next; 
return 
node; 

void operator delete(void* space, size_t bytes) 

if (bytes != sizeof(Foo)) 
return 
::operator 
delete(space); 
((FreeNode*)space)->next = fgFreeList; 
fgFreeList 

(FreeNode*)space; 

}; 
Указатель v-таблицы гарантирует, что каждый 
Foo
по крайней мере не меньше указателя на 
следующий элемент списка (
FreeNode*
), а виртуальный деструктор обеспечивает правильность 
размера, передаваемого оператору 
delete

Повторяю: рассмотренная схема управления памятью не предназначена для практического применения 
(встретив производный класс, она собирает вещи и отправляется домой). Она лишь демонстрирует 
некоторые базовые принципы перегрузки операторов 
new
и 
delete

Наследование операторов new и delete 
Если перегрузить операторы 
new
и 
delete
для некоторого класса, перегруженные версии будут 
унаследованы производными классами. Ничто не помешает вам снова перегрузить 
new
и/или 
delete
в 
одном из этих производных классов. 
class Bar : public Foo { 
public: 
virtual ~Bar(); 
// Foo::~Foo тоже должен быть виртуальным 
void* operator new(size_t bytes); 
void operator delete(void* space, size_t bytes); 
}; 
С виртуальным деструктором все работает. Если деструктор не виртуальный, в следующем фрагменте 
будет вызван правильный оператор 
new
и оператор 
delete
базового класса: 
Foo* foo = new Bar; 
delete foo; 
Хотя этот фрагмент работает, подобное переопределение перегруженных операторов обычно считается 
дурным тоном. Во всяком случае в кругу знатоков С++ о таких вещах не говорят. Когда производный 
класс начинает вмешиваться в управление памятью базового класса, во всей программе начинают 
возникать непредвиденные эффекты. Если вам захочется использовать несколько стратегий управления 
памятью в одной иерархии классов, лучше сразу включить нужную стратегию в конкретный 
производный класс средствами множественного наследования, чем унаследовать ее и потом заявить в 
производном классе: «Ха-ха, я пошутил». 



Download 1,95 Mb.

Do'stlaringiz bilan baham:
1   ...   105   106   107   108   109   110   111   112   ...   144




Ma'lumotlar bazasi mualliflik huquqi bilan himoyalangan ©hozir.org 2024
ma'muriyatiga murojaat qiling

kiriting | ro'yxatdan o'tish
    Bosh sahifa
юртда тантана
Боғда битган
Бугун юртда
Эшитганлар жилманглар
Эшитмадим деманглар
битган бодомлар
Yangiariq tumani
qitish marakazi
Raqamli texnologiyalar
ilishida muhokamadan
tasdiqqa tavsiya
tavsiya etilgan
iqtisodiyot kafedrasi
steiermarkischen landesregierung
asarlaringizni yuboring
o'zingizning asarlaringizni
Iltimos faqat
faqat o'zingizning
steierm rkischen
landesregierung fachabteilung
rkischen landesregierung
hamshira loyihasi
loyihasi mavsum
faolyatining oqibatlari
asosiy adabiyotlar
fakulteti ahborot
ahborot havfsizligi
havfsizligi kafedrasi
fanidan bo’yicha
fakulteti iqtisodiyot
boshqaruv fakulteti
chiqarishda boshqaruv
ishlab chiqarishda
iqtisodiyot fakultet
multiservis tarmoqlari
fanidan asosiy
Uzbek fanidan
mavzulari potok
asosidagi multiservis
'aliyyil a'ziym
billahil 'aliyyil
illaa billahil
quvvata illaa
falah' deganida
Kompyuter savodxonligi
bo’yicha mustaqil
'alal falah'
Hayya 'alal
'alas soloh
Hayya 'alas
mavsum boyicha


yuklab olish