Информационный сайт

 

Реклама
bulletinsite.net -> Книги на сайте -> Программисту -> Мизрохи С.В. -> "Turbo Pascal и объектно-ориентированное программирование" -> 59

Turbo Pascal и объектно-ориентированное программирование - Мизрохи С.В.

Мизрохи С.В. Turbo Pascal и объектно-ориентированное программирование — М.: Финансы и статистика , 1992. — 192 c.
ISBN 5-279-00903-2
Скачать (прямая ссылка): efektispolzc2000.djvu
Предыдущая << 1 .. 53 54 55 56 57 58 < 59 > 60 61 62 63 64 65 .. 105 >> Следующая

// Это файл example.п.
inline void f () { . . . } // Определение f. // Это файл sourcel.cpp.
ttinclude "example.h" Il Включает определение f.
Il Содержит вызовы f.
Il Это файл source2.cpp #include "example.h"
11 Тоже включает определение f. Il Также вызывает f.
W
^^пия
Правило 33
В данном случае вы оказываетесь в достаточно парадоксальной ситуации, ког-Aa вызываемая функция f является встраиваемой, но в соответствии со старыми
В соответствии со старым правилом невстраиваемых inline-функций, если f не встраивается при компиляции sourcel. срр, то результирующий объектный файл содержит функцию, называемую f, так, как будто f и не определялась inline. Аналогично при компиляции sou.rce2.cpp генерируемый объектный файл также будет содержать функцию, называемую f. Когда вы попытаетесь скомпоновать эти два объектных файла, компоновщик вполне обоснованно выдаст вам сообщение: программа содержит два определения f, что является ошибкой.
Для разрешения данной проблемы старые правила требуют, чтобы компиляторы рассматривали невстраиваемые inline-функции так, как будто они были объявлены со static, то есть локально для компилируемого в данный момент файла. В только что рассмотренном примере компилятор, следуя старым правилам, при компиляции файла sour с el. срр будет рассматривать f как статическую функцию так же, как и при компиляции source2 . срр. Такой подход разрешает проблему компоновки, но «не бесплатно»: каждая единица трансляции, содержащая определение (и вызывающая f), содержит свою собственную статическую копию f. Если в f входит объявление статических переменных, то каждая копия также содержит и свою собственную копию переменных, что, несомненно, удивит программистов, полагающих, что static внутри функции означает «только один экземпляр». - j Это ведет к удивительному открытию. И в соответствии со старыми, И В COOT-J ветствии с новыми правилами, если встраиваемая функция не встраивается, вы все равно «платите» за функциональный вызов, а в соответствии со старым правилом можете даже увеличить размер объектного файла, поскольку каждая еди-J ница трансляции, содержащая вызов f, содержит также свою копию кода f и ее!
тических переменных! (Еще больше положение осложняет то, что каждая ко-ия f со своими статическими переменными имеет тенденцию располагаться на отдельной странице виртуальной памяти, поэтому обращения к различным копиям f могут повлечь за собой один или несколько страничных отказов - page fault.)
Более того, иногда вашим бедным компиляторам приходится генерировать Тело встраиваемой функции даже в тех случаях, когда они всей душой хотели бы ее встроить. В частности, если ваша программа берет адрес встраиваемой функции, то компилятор должен генерировать ее тело. Как иначе вы можете получить Указатель функции, когда она не существует?
inline void f() {...} Il Как выше.
void (*pf) () = f; II pf указывает на f.
int main ()
{
f О ; Il Встраиваемый вызов f.
pf О ; Il Невстраиваемый вызов f посредством pf.
}
Классы и функции: реализация
правилами каждая единица трансляции, требующая адреса f, будет генерировать статическую копию функции. (В соответствии с новыми правилами независимо от количества единиц трансляции будет сгенерирована единственная невстраива-емая копия f.)
Вы можете столкнуться с такими певстраиваемыми функциями inline, даже если никогда не используете указатели на функции, поскольку не только программисты могут запросить указатель функции. Иногда это делают и компиляторы. В частности, иногда они генерируют невстраиваемые копии конструкторов и деструкторов, чтобы можно было получить указатели на эти функции для использования при создании и удалении массивов объектов класса.
На самом деле конструкторы и деструкторы, в противоположность тому, что вы можете подумать о них в первый момент, - зачастую наихудшие кандидаты для встраивания. Для примера рассмотрим конструктор следующего класса Derived:
class Base { public:
private:
string bml, bm2; Il Члены базового класса 1 и 2.
};
class Derived: public Base { public:
Derived() {} Il Конструктор Derived пуст.
... Il Или все-таки нет?
private:
string dml, dm2, dm3; Il Члены производного класса 1-3.
};
Данный конструктор выглядит, как идеальный кандидат для встраивания, поскольку не содержит кода. Но это впечатление не всегда верно. Хотя внешне кажется, что конструктор пуст, в действительности он может содержать достаточно большое количество кода.
При создании и удалении объектов С++ дает целый ряд гарантий. Правило 5 описывает, как при использовании new динамически создаваемые объекты автоматически инициализируются конструкторами, и как при использовании delete вызываются соответствующие деструкторы. Правило 13 объясняет, что когда вы создаете объект, автоматически создается каждый базовый класс и каждый член класса, а удаление объектов автоматически влечет обратный процесс. В правилах 5 и 13 изложено, что в таком случае предписывает С++, хотя и ничего не «говорит» о том, как именно это должно произойти. Решение остается за разработчиками компиляторов, и совершенно ясно, что все автоматические действия не могут происходить сами по себе. В вашей программе должен существовать код, реализующий такое поведение, - код, написанный разработчиком компилятора и вставляемый в программу при компиляции; и этот код требуется где-нибудь разместить. Иногда он попадает в конструкторы и деструкторы, поэтому некоторые компиляторы генерируют для якобы пустого конструктора класса Derived в вышеприведенном примере код, эквивалентный следующему:
Предыдущая << 1 .. 53 54 55 56 57 58 < 59 > 60 61 62 63 64 65 .. 105 >> Следующая
Реклама
Авторские права © 2009 AdsNet. Все права защищены.
Rambler's Top100