|
Программирование >> Оптимизация возвращаемого значения
bool HeapTracked::isOnHeap{) const { Получить указатель на начало памяти, занятой *this; подробнее см. ниже. const void *rawAddress = dynamic cast<const void*>{this) Поиск указателя в списке адресов, возвращенных operator new. list<RawAddress>::iterator it = find{addresses.begin{), addresses.end(), rawAddress); return it != addresses.end0 ; Вернуть it, если } адрес был найден. Приведенный код довольно прост, хотя если вы не знакомы с классом list и другими компонентами стандартной библиотеки шаблонов, он может показаться вам сложным. В правиле 35 все это объясняется подробно, хотя комментариев в самом коде должно быть достаточно, чтобы объяснить, что происходит в примере. Вас может поставить в тупик только следующий оператор (в функции isOnHeap): const void *rawAddress = dynamic cast<const void*>(this); Как я уже упоминал, написание глобальной функции isSafeToDelete усложняется из-за того, что объекты с несколькими или виртуальными базовыми классами имеют несколько адресов. Эта проблема досаждает и в функции isOnHeap, но поскольку функция isOnHeap применяется только к объектам HeapTracked, можно использовать особенность оператора dynamic cast (см. правило 2). Оператор dynamic cast, если его просто применить к указателю на void* (или const void*, или volatile void*, или - когда не хватает модификаторов - const volatile void*), дает указатель на начало памяти объекта, на который он ссылается. Но оператор dynamic cast применим только к указателям на объекты, имеющие хотя бы одну виртуальную функцию. Наша злосчастная функция isSaf eToDelete должна была работать со всеми типами указателей, поэтому оператор dynamic cast не помог бы ей. Функция isOnHeap является более избирательной (она проверяет только указатели на объекты типа HeapTracked), поэтому в ней приведение this к const void* при помощи оператора dynamic cast дает указатель на начало памяти текущего объекта. Это указатель, который теперь должна возвращать функция HeapTracked: : operator new, если память для текущего объекта была первоначально выделена при помощи HeapTracked: : operator new. Если ваши компиляторы поддерживают оператор dynamic cast, то предложенный метод является совершенно переносимым. Имея этот класс, даже программисты на Basic могут отслеживать указатели на объекты в куче. Для этого достаточно сделать класс наследником класса HeapTracked. Если, например, требуется знать, является ли указатель на объект типа Asset указателем на объект в куче, можно изменить определение класса Asset, задав HeapTracked в качестве базового класса: class Asset: public HeapTracked { private: Недостаток смешанного класса, такого как HeapTracked, состоит в том, что он не может использоваться со встроенными типами, поскольку типы, подобные int и char, не способны наследовать от чего бы то ни было. Но обычно классы вроде HeapTracked создаются благодаря возможности определить, допускается ли выполние операции delete this, а такие операции никогда не нужно применять к встроенным типам, поскольку последние не имеют указателя this. Запрет создания объектов в куче На этом закончим обсуждение того, как можно определить, находится ли объект в Kje. Теперь поговорим о предотвращении создания объектов в куче. Как обычно, имеется три случая: объекты, которые создаются непосредственно, объекты, создаваемые как части базового класса в производных классах, и объекты, встроенные в другие объекты. Рассмотрим каждый из них поочередно. Запретить прямое создание объектов в Kje легко, поскольку такие объекты всегда создаются при помощи вызова new, и можно сделать, чтобы клиенты не могли вызвать new. Нельзя ограничить доступность оператора new (который является встроенным), но можно воспользоваться тем, что он всегда вызывает функцию operator new (см. правило 8), а эту функцию допускается переопределить. В частности, ее удобно объявить как private. Например, если вы хотите запретить клиентам создание объектов типа UPNumber, то вот один из способов: class UPNumber { private: static void *operator new(size t size); static void operator delete(void *ptr); Пользователи могут сделать только то, что им разрешено: UPNumber nl; static UPNumber п2; UPNumber *p = new UPNumber; Нормально. Также нормально. Ошибка! попытка вызвать закрытую функцию operator new. UPNumber value; Затем можно опрашивать указатели Asset* следующим образом: void inventoryAsset (const Asset *ap) { if (ap->isOnHeap()) { ap находится в куче - занести его в список } else { ар не находится в куче - записать это Достаточно объявить функцию operator new закрытой. Вообще кажется странным делать operator new открытой, оставляя функцию operator delete закрытой, поэтому если нет особой необходимости разбивать пару этих функций, лзше всего объявлять их в одной части класса. Если вам также нужно запретить создание в Kje массивов объектов типа UPNumber, то вы также можете объявить закрытыми функции operator new [ ] и operator delete [ ] (см. правило 8). Интересно, что объявление функции operator new закрытой также предотвращает создание объектов типа UPNumber как части базового класса в производных классах. Это происходит потому, что функции operator new и operator delete наследуются, поэтому если данные функции не объявлены открытыми в производном классе, то класс наследует закрытые версии, объявленные в базовом классе: class UPNumber { ... ) ; class NonNegativeUPNumber: public UPNumber { NonNegativeUPNumber nl; static NonNegativeUPNumber n2; NonNegativeUPNumber *p = new NonNegativeUPNumber; Как и выше. Предположим, что в этом классе не объявляется функция operator new. Нормально. Также нормально. Ошибка! Попытка вызывать закрытую функцию operator new. Если в производном классе объявляется собственная функция operator new, то она будет вызываться при создании объектов производного класса в Kje, и придется найти другой способ предотвратить.попадание в Kjiy объектов типа UPNumber как частей базового класса. Аналогично то, что функция operator new класса UPNumber является закрытой, не влияет на попытки создания в Kjie объектов, содержащих объекты типа UPNumber в качестве членов класса: class Asset { public: Asset(int initValue); private: UPNumber value; Asset *pa = new Asset (100) ; Нормально, вызывает Asset::operator new или : :operator new, a не UPNumber::operator new. По сути это возвращает вас в точку, где вы находились, когда собирались генерировать исключение в конструкторах UPNumber, если объект типа UPNumber создавался в памяти, которая не находится в Kje. На этот раз, конечно, хотелось бы генерировать исключение, если данный объект находится в Kje. Но так же, как не существует переносимого способа определить, находится ли адрес в куче.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |