|
Программирование >> Оптимизация возвращаемого значения
вы наверняка отвергнете этот соблазнительный, но ненадежный трюк со сравнением адресов. Печальный факт состоит в том, что не только не существует переносимого способа определить, находится ли объект в куче, не существует также и полупереносимого способа, который работал бы в большинстве случаев. Если вам совершенно необходимо определить, находится ли адрес в Kjie, придется обратиться к непереносимым, зависящим от реализации системным вызовам, и это все, что можно сделать. Вам лучше попытаться изменить структуру программы, чтобы не было столь необходимо знать, находится ли объект в куче. От ответа на вопрос, где расположен объект, зависит, безопасно ли вызывать для него оператор delete? Часто такое удаление принимает форму печально известного оператора delete this. Однако возможность безопасно удалить указатель говорит не только о том, что он ссылается на что-то в куче, поскольку не для любого указателя на объект в куче можно безнаказанно вызвать оператор delete. Рассмотрим снова объект Asset, содержащий объект UPNumber: class Asset { private: UPNumber value; Asset *pa = new Asset; Очевидно, что объект *pa (включая его элемент value) находится в куче. Так же очевидно, что небезопасно вызывать оператор delete для указателя на ра-> value, так как этот указатель не был пол5ен в результате вызова оператора new. К счастью, легче определить, можно ли безопасно удалить указатель, чем выяснить, указывает ли он на что-либо в куче, так как все, что нужно для ответа на первый вопрос, - это набор адресов, которые были возвращены функцией operator new. Поскольку можно написать функцию operator new самостоятельно, то легко создать такой набор, например, следующим образом: void *operator new(size t size) { void *p = getMemory (size) ; Вызвать функцию для выделения памяти и обработки событий нехватки памяти. добавить р к набору выделенных адресов; return р; void operator delete(void *ptr) { releaseMemory(ptr); Освободить память. удалить ptr из набора выделенных адресов памяти; bool isSafeToDelete(const void *address) { вернуть результат в зависимости от того, находится ли адрес в наборе выделенных адресов Это почти так же просто, как и возвращаемое функцией значение. Функция operator new добавляет элементы в набор выделенных адресов, функция operator delete удаляет элементы, а функция isSaf eToDelete просматривает набор, определяя, находится ли в нем заданный адрес. Если функции operator new и operator delete являются глобальными, то эта схема будет работать для всех типов, даже для встроенных. На практике три вещи могут ослабить ваш энтузиазм. Первой из них является общее крайнее нежелание определять что-либо глобально, в особенности такие функции с предопределенным значением, как operator new и operator delete. Зная, что существует только одно глобальное пространство имен и только одна версия функций operator new и operator delete с обычными сигнатурами (то есть наборами типов параметров) в этом пространстве, меньше всего хотелось бы захватывать данные сигнатуры для собственного использования. При этом код стал бы несовместимым с любым другим, где также реализованы глобальные версии operator new и operator delete (такими являются многие объектно-ориентированные системы баз данных). Второе соображение касается эффективности: зачем обременять все операции выделения памяти из kjh накладными расходами на отслеживание возвращаемых адресов, если это не нужно? Последнее соображение является неинтересным, но важным. Оказывается, невозможно реализовать функцию isSaf eToDelete так, чтобы она работала во всех случаях: объекты с несколькими или виртуальными базовыми классами имеют несколько адресов, поэтому нет гарантии, что адрес, переданный функции isSaf eToDelete, тот же самый, который был возвращен функцией operator new, даже если данный объект и размещался в Kjie. Дополнительные сведения по этому вопросу см. в правиле 24 и 31. В действительности хотелось бы обеспечить выполнение указанных функций без сопутствующего загрязнения глобального пространства имен, дополнительных расходов и проблем с правильностью работы. К счастью, язык С++ позволяет сделать в точности то, что нужно, при помощи абстрактного смешанного базового класса (abstract mixin base class). Абстрактный базовый класс - это класс, экземпляры которого создавать нельзя, то есть класс, имеющий хотя бы одну абстрактную функцию*. Смешанный класс - класс, обеспечивающий выполнение одной определенной функции, разработанный так, чтобы быть совместимым с любыми другими функциями, которые может обеспечивать наследующий класс. Такие классы почти всегда являются абстрактными. Поэтому можно создать абстрактный смешанный базовый класс, позволяющий производным классам определять, был ли указатель возвращен функцией operator new: * В оригинале абстрактные методы названы полностью виртуальными . (Прим. ред.) class HeapTracked { Смешанный класс; отслеживает public: указатели, возвращаемые operator new. class MissingAddress{}; Класс исключений; см. ниже. virtual -HeapTracked{) = 0; static void *operator new(size t size) ; static void operator delete(void *ptr); bool isOnHeapO const; private: typedef const void* RawAddress; static list<RawAddress> addresses; Этот класс использует для отслеживания указателей, возвращаемых функцией operator new, структуру list, которая является частью стандартной библиотеки C-I-+ (см. правило 35). Данная функция выделяет память и добавляет элементы в список; функция operator delete возвращает память и удаляет элементы из списка; а функция isOnHeap сообщает, находится ли адрес объекта в списке. Реализация класса HeapTracked проста, так для выделения и возврата памяти вызываются глобальные функции operator new и operator delete, а класс list содержит функции для вставки, удаления и поиска элементов в списке. Вот полная реализация класса HeapTracked: обязательное определение статического элемента класса. list<RawAddress> HeapTracked::addresses; Деструктор класса HeapTracked абстрактный, чтобы сделать класс абстрактным. Но деструктор должен быть определен, поэтому приводится пустое определение. HeapTracked::-HeapTracked() {} void * HeapTracked: :operator new(size t size) void *memPtr = : :operator new(size) ; Получить память, addresses. push front (memPtr); Поместить ее адрес в начало списка. return memPtr; void HeapTracked::operator delete(void *ptr) { Получить итератор , определяющий элемент списка и содержащий ptr; подробности см. в правиле 35. list<RawAddress>::iterator it = find{addresses.begin(), addresses.end(), ptr); if (it != addresses.end() ) { Если элемент был найден, addresses.erase (it); удалить его, освободить ::operator delete(ptr); память; } else { иначе ptr не был выделен throw MissingAddress () ; при помощи operator new, } поэтому сгенерировать } исключение.
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |