Программирование >>  Оптимизация возвращаемого значения 

1 ... 46 47 48 [ 49 ] 50 51 52 ... 96


типа HeapConstraintViolation. Иначе конструктор продолжает работу как обычно, и когда завершится создание объекта, флаг onTheHeap устанавливается равным false, что соответствует значению по умолчанию для следующего создаваемого объекта.

Хотя это и хорошая идея, она не сработает. Рассмотрим возможный клиентский код:

UPNumber *питЬегАггау = new UPNumber[100];

Первая проблема заключается в том, что память для массива выделяется при помощи operator new [ ], а не operator new, но первую функцию (если ее поддерживают ваши компиляторы) можно написать так же просто, как и вторую. Более проблематично, что массив numberArray содержит 100 элементов, поэтому конструктор будет вызван 100 раз. Однако память будет выделена всего единожды, поэтому флаг onTheHeap примет значение true только для первого из этих 100 конструкторов. При вызове второго конструктора будет сгенерировано исключение, и горе вам!

Даже без применения массивов работа с флагами может закончиться неудачей. Рассмотрим оператор:

UPNumber *рп = new UPNumber (*new UPNumber) ;

Здесь в куче создаются два объекта класса UPNumber, и переменная рп указывает на один из них; она инициализируется значением второго из объектов. Этот код приводит к утечке ресурсов, но пока проигнорируем ее и рассмотрим, что происходит при выполнении следующего выражения:

new UPNumber (*new UPNumber)

Оно содержит два вызова оператора new и, следовательно, два вызова operator new, а также два вызова конструктора UPNumber (см. правило 8). Программисты обычно ожидают, что эти функции будут выполняться поэтапно:

1. Вызвать функцию operator new для первого объекта.

2. Вызвать конструктор для первого объекта.

3. Вызвать operator new для второго объекта.

4. Вызвать конструктор для второго объекта.

Но спецификация языка не гарантирует такой порядок выполнения. Некоторые компиляторы вместо этого генерируют вызовы функций следующим образом:

1. Вызвать функцию operator new для первого объекта.

2. Вызвать функцию operator new для второго объекта.

3. Вызвать конструктор для второго объекта.

4. Вызвать конструктор для первого объекта.

Компиляторы, которые генерируют такой код, не содержат ошибки, но прием с установкой флага и operator new здесь не работает. Это связано с тем, что флаг устанавливается на 1-ом и 2-ом шаге и сбрасывается на 3-ем шаге, поэтому объект, создаваемый на 4-ом шаге, считает, что он находится не в куче, даже если это и не так.



Подобные затруднения не сводят на нет основную идею проверки в конструкторе, находится ли *this в куче. Они лишь показывают, что проверка флага, устанавливаемого в operator new (или operator new[ ] ), является не самым лучшим способом пол5ения этой информации. Нужен более надежный способ.

Если вы достаточно безрассудны, то перед вами может возникнуть искушение поиграть с несовместимостью. Например, вы можете использовать то, что во многих системах адресное пространство программы организовано в виде линейной последовательности адресов, и стек программы растет вниз от вершины адресного пространства, а Kja поднимается снизу (см. рис. 5.2).

Адресное пространство программы (неполная картина)

(Растет вниз)

Куча (F III <[ > I )

Старшие адреса

Младшие адреса

Рис. 5.2

Вы можете полагать, что в системах, где память программы организована таким образом (а это немалая часть приложений), будет удобно использовать следующую функцию для проверки, находится ли определенный адрес в Kje:

Некорректная попытка определить, / / находится ли адрес в куче. bool опНеар(const void *address) {

char onTheStaclt; Локальная стековая переменная,

return address < &onTheStaclt;

Рассуждения, лежащие в основе этого подхода, достаточно интересны. Переменная onTheStack является локальной переменной функции опНеар и поэтому находится в стеке. При вызове функции опНеар ее кадр стека (то есть ее запись активации) будет помещена на вершину стека программы, и поскольку стек в такой архитектуре растет вниз (в сторону младших адресов), то адрес переменной onTheStack должен быть меньше адреса любых других переменных или объектов, находящихся в стеке. Если параметр address меньше, чем положение переменной onTheStack, то он не может находиться в стеке, следовательно, он находится в Kje.

Все эти рассуждения прекрасны, но они многого не уштыъжп:. Основная проблема заключается в том, что объекты могут располагаться в трех различных областях, а не в двух. Да, объекты могут находиться в стеке и куче, но не будем забывать



о статических объектах. Статические объекты - это объекты, которые инициализируются при выполнении программы только один раз. Статические объекты включают в себя не только объекты, объявленные как static, но также объекты, объявленные в глобальном или других пространствах имен. Эти объекты должны где-то находиться, и они находятся не в стеке и не в куче.

Где они размещаются, зависит от системы, но на многих системах, в которых стек и куча растут навстречу друг другу, они располагаются под кучей. Предыдущая схема организации памяти, хотя и частично (а на некоторых системах и полностью) соответствует действительности, все же не является универсальной. Если включить в схему статические объекты, она примет вид, представленный на рис. 5.3.

Адресное пространство программы / (полная \ картина)

CicK (Расютвниз)

Куча

Сга1ическис объекты

Старшие адреса

Младшие адреса

Рис. 5.3

Неожиданно становится очевидным, почему функция опНеар не будет работать даже на системах, для которых предназначена: она не может различать объекты в куче и статические объекты;

void allocateSomeObjects() {

char *рс = new chat; char с;

static char sc;

Объект в куче: опНеар(рс)

вернет значение true.

Объект в стеке: опНеар(&рс)

вернет значение false.

Статический объект: onHeap(&sc)

вернет значение true.

Если вы во что бы то ни стало хотите найти способ отличать объекты в кзе от объектов в стеке, то в своем отчаянии можете пойти на сделку с дьяволом переносимости, но настолько ли вы безрассудны, чтобы заключить сделку, которая все равно не гарантирует получение правильных ответов? Конечно же, нет, поэтому



1 ... 46 47 48 [ 49 ] 50 51 52 ... 96

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика