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



Download 1,95 Mb.
Pdf ko'rish
bet122/144
Sana24.02.2022
Hajmi1,95 Mb.
#223123
TuriРеферат
1   ...   118   119   120   121   122   123   124   125   ...   144
Bog'liq
C -Eldjer-Djeff-for-Real-Programmers-RUS-www.itlibitum.ru

 
219 
Размещение v-таблицы в начале объекта приводит к тому, что принадлежащие 
C
реализации 
виртуальных функций, объявленных в 
A
, останутся доступными, но будут иметь те же смещения, что и 
для 
A
. Работая с 
C*
, компилятор знает полную структуру всего объекта и может обращаться к членам 
A

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

B
B*(c)
На самом деле v-таблиц две. Одна находится в начале объекта и содержит все виртуальные функции, 
первоначально объявленные в 
A
или 
C
, а другая — в начале компонента 
B
и содержит виртуальные 
функции, объявленные в 
B
. Это означает, что преобразование типа от производного к базовому классу 
в С++ может при некоторых обстоятельствах породить указатель на середину объекта (по аналогии с 
указателями на переменные класса, о которых говорилось выше). Кроме того, в С++ открывается 
возможность дурацких фокусов: 
C* anotherC = C*(void*(B*(c))); 
anotherC->MemberOfC(); 
Видите, в чем проблема? Преобразование 
B*(c)
смещает указатель. Затем он преобразуется к типу 
void*
. Далее следует обратное преобразование к 
C*
— и наша программа будет уверена, что 
C
начинается с неверного адреса. Без преобразования к 
void*
все работает, поскольку компилятор может 
опеределить смещение 
B*
в 
C*
. В сущности, преобразование от 
base*
к 
derived*
(где 
base
— 
базовый, а 
derived
— производный класс) выполняется каждый раз, когда клиент вызывает 
виртуальную функцию 
B
, переопределенную в 
C
. Но когда происходит преобразование от 
void*
к 
C*

компилятор лишь наивно полагает, что программист действует сознательно. 
Запомните: каждый программист на С++ за свою карьеру проводит как минимум одну бессонную ночь, 
пытаясь понять, почему его объект бредит. Потом приходит какой-нибудь гуру, с ходу ставит диагноз 
«синдром класс-
void
-класс» — притом так, чтобы слышали окружающие — и разражается злорадным 
смехом. Впрочем, я отклонился от темы. 
Виртуальные базовые классы 
Если вы пользуетесь виртуальными базовыми классами, попрощайтесь со всеми схемами уплотнения и 
сборки мусора, требующими перемещения объектов в памяти. Ниже приведен фрагмент программы и 
показано, как объект представлен в памяти. 
class Base {...}; 
class A : virtual public Base {...}; 
class B : virtual public Base {...}; 
class Foo : public A, public B {...}; 
Тьфу. Компилятору так стыдно, что 
Base
приходится реализовывать как виртуальный базовый класс, 
что он прячет его как можно дальше, под 
Foo

A
и 
B
содержат указатели на экземпляр 
Base
… да, все 
верно, указатели, то есть непосредственные адреса в памяти. Вы не имеете доступа к этим указателям 
и, следовательно, не сможете обновить их при перемещении объекта в памяти. 
B
A
Foo
Base


 220 
Указатель на переменную класса 
Идея указателя на переменную класса заключается в том, что переменную можно однозначно 
идентифицировать не по ее непосредственному адресу, но по адресу содержащего ее объекта и 
смещению переменной внутри объекта. Если вы никогда не пользовались указателями на переменные 
класса, изучите следующий фрагмент как можно внимательнее. 
class Foo { 
private: 
int 
x; 
public: 
static int& Foo::*X() { return &Foo::x; } 
}; 
Foo f = new Foo; 
// Создать экземпляр 
int& Foo::*pm = Foo::X(); 
// Вычислить смещение int 
int& i = f->*pm; 
// Применить смещение к экземпляру 
Функция 
X()
возвращает не ссылку на 
int
, а смещение некоторого 
int
в экземплярах класса 
Foo

Функция 
Foo::X()
объявлена статической, поскольку относится не к конкретному экземпляру, а к 
классу в целом. Команда 
return &Foo::x;
определяет смещение конкретной переменной, 
x
. В строке 
int& Foo::*pm = Foo::X();
объявляется переменная 
pm
, которая содержит смещение переменной 
int
класса 
Foo
. Она инициализируется смещением, полученным от 
Foo::X()
. Наконец, в строке 
int& 
i = f->*pm;
смещение применяется к конкретному экземпляру для вычисления адреса конкретного 
int. Обратите внимание: значение pm само по себе бесполезно до тех пор, пока вы не примение его к 
объекту. 
Все эти 
int&
с таким же успехом можно заменить на 
int*
. В любом случае все завершается 
косвенным получением адреса некоторой части объекта так, словно вы получили явный адрес 
переменной класса. Указатели на члены классов также могут применяться для косвенных ссылок на 
функции, а не на переменные класса, но это не относится к нашей теме — управление памятью. К тому 
же я не хочу взваливать на себя лишнюю головную боль. 
vtable
x
y
f
pm = Foo::X()
F->*pm
Последствия 
Все сказанное обладает фундаментальными последствиями для управления памятью. Чтобы 
переместить объект в памяти, вам придется проследить за тем, чтобы перемещался вмещающий объект 
верхнего уровня, а не некоторый вложенный объект, адрес которого у вас имеется. Более того, при 
перемещении объекта придется обновлять все указатели — не только на сам объект, но и на все 
вложенные объекты и базовые классы. 
Если вы хотите узнать, существуют ли ссылки на некоторый объект, придется искать указатели не 
только на начало объекта, но и на все его переменные и базовые классы. 
Поиск указателей 
Итак, теперь мы знаем, с какими разными указателями нам придется иметь дело. Как же отыскать их 
все? Чтобы переместить объект, нам придется обновить все указатели на него. Чтобы понять, доступен 
ли объект, придется собрать все указатели. 
Специализированные пространства памяти для указателей 
Одно из «силовых» решений — сложить все указатели в одно место, где их будет легко найти. В свою 
очередь, это подразумевает, что все указатели должны быть умными и храниться в специальных 
пространствах памяти. Эти пространства должны быть организованы так, чтобы вы могли перебрать их 



Download 1,95 Mb.

Do'stlaringiz bilan baham:
1   ...   118   119   120   121   122   123   124   125   ...   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