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



Download 1,95 Mb.
Pdf ko'rish
bet32/144
Sana24.02.2022
Hajmi1,95 Mb.
#223123
TuriРеферат
1   ...   28   29   30   31   32   33   34   35   ...   144
Bog'liq
C -Eldjer-Djeff-for-Real-Programmers-RUS-www.itlibitum.ru

 
41 
class Bar : public Foo { 
private: 
int* 
numbers; 
public: 
Bar() : numbers(new int[17]) {...} 
~Bar(); 
}; 
Bar* b = new Bar; 
delete b; // 
Вызывает Bar::~Bar() 
Foo* f = new Bar; 
delete f; // 
Ой! Вызывается Foo::Foo()! 
При удалении 
f
массив, на который ссылается переменная numbers, превращается в некое подобие 
Летучего Голландца, обреченного на вечные скитания в памяти. Чтобы избежать беды, достаточно 
объявить оба деструктора виртуальными; в этом случае независимо от типа указателя (кроме, конечно, 
void*
) уничтожение будет начинаться с 
Bar: :~Ваг()

Другая, более коварная проблема с невиртуальными деструкторами возникает при организации 
нестандартного управления памятью. Компилятор сообщает вашему перегруженному оператору 
размер уничтожаемого объекта — сюрприз! Для невиртуального деструктора этот размер может 
оказаться неверным. Представьте себе удивление вашей программы, когда ей сообщат, что объект 
имеет размер 20 байт, хотя на самом деле он равен 220 байтам! Разработчики компиляторов C++ любят 
похвастаться подобными проделками за кружкой пива после работы. 
Мораль: деструкторы следует делать виртуальными. Исключение составляют ситуации, когда ваш 
класс или структура не имеет производных классов или у вас найдутся чрезвычайно веские причины 
поступить иначе. 
Прямой вызов деструкторов 
Деструктор можно вызвать и напрямую, не прибегая к оператору 
delete
, поскольку это такая же 
функция, как и все остальные. Впрочем, до того, как мы займемся нестандартным управлением 
памятью, вряд ли это будет иметь какой-нибудь смысл. 
class Foo { 
public: 
~Foo(); 
}; 
Foo* f = new Foo; 
f->Foo::~Foo(); 
Позднее мы воспользуемся этой возможностью, а пока сохраните ее в своей коллекции C++. 
Присваивание 
Присваивание одного объекта другому в C++ — дело серьезное. Впрочем, в обилии запутанных правил 
есть и положительная сторона — благодаря им вы постоянно остаетесь начеку и уделяете больше 
внимания программе. 
Синтаксис и семантика присваивания 
Для присваивания одного объекта другому используется оператор 
=

Foo f; 
Foo f1; 
f1 = f; 
Присваивание выполняется в третьей строке. Если бы 
f
и 
f1
были целыми или чем-нибудь столь же 
простым, смысл этой строки был бы предельно ясен: содержимое области памяти, на которую 


 42 
ссылается 
f
, копируется в область памяти, на которую ссылается 
f1
. Только и всего. Но если 
Foo
относится к нетривиальному классу, в C++ все заметно усложняется. В приведенном примере 
компилятор предоставляет оператор 
=
по умолчанию, который вызывается для выполнения 
фактического копирования. Как и с конструкторами копий, вы можете спокойно сидеть и смотреть, как 
компилятор вкалывает за вас, или написать свой собственный оператор 
=
. То, что делает версия по 
умолчанию, вам может и не понравиться, особенно в момент освобождения памяти деструктором 
класса. 
class String { 
private: 
char* 
s; 
public: 
String(char*); 
~String(); 
void Dump(ostream& os); 
}; 
String::String(char* str) : s(NULL) 

if (str == NULL) {
// NULL означает пустую строку 
s = new char[1]; 
*s = ‘\0’; 

else 

s = new char[strlen(str) + 1]; 
strcpy(s, 
str); 


String::~String() 

delete 
s; 

void String::Dump(ostream& os) 

os << “\”” << s << “\””; 

String* s1 = new String(“Hello”); 
String* s2 = new String(“Goodbye”); 
s2 = s1; 
delete s1; 
// Память освободилась, вроде все нормально... 
s2->Dump(); // 
Облом! Ха-ха-ха! 
delete s2; 
// Помогите, убивают! Ха-ха-ха! 
По умолчанию компилятор копирует содержимое 
s2->s
поверх содержимого 
s1->s
. При этом 
копируется значение указателя, а не символы, поэтому после присваивания возникают две большие 
проблемы. Два разных объекта ссылаются на одну область памяти, и никто не ссылается на копию 
Goodbye
, созданную командой 
String* s2 = new String( "Goodbye");
. Дальше — больше; при 
удалении 
s1
деструктор освобождает область памяти, на которую ссылается 
s1
. Однако на эту память 
продолжает ссылаться указатель 
s2->s
. Попытка вывести 
s2->s
дает совершенно безумные 
результаты. «Комедия ошибок» достигает кульминации при попытке удалить 
s2
, поскольку менеджер 
памяти попытается освободить ранее освобожденную область. Чего только не бывает в C++! 



Download 1,95 Mb.

Do'stlaringiz bilan baham:
1   ...   28   29   30   31   32   33   34   35   ...   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