Программирование >>  Синтаксис инициирования исключений 

1 ... 19 20 21 [ 22 ] 23 24 25 ... 82


Во что обходится умный указатель?

Объект класса, не содержащего виртуальных функций, занимает столько места, сколько необходимо для хранения всех его переменных. В рассмотренных выше умных указателях используется всего одна переменная - *-указатель; то есть размер умного указателя в точности совпадает с размером встроенного указателя. Хороший компилятор C++ должен специальным образом обрабатывать тривиальные подставляемые функции, в том числе и находящиеся в шаблоне умного указателя.

template <c1ass Type> class Ptr { private:

Type* pointer; public:

Ptr() : pointer(NULL) {}

Ptr(Type* p) : pointer(p) {}

operator Type*() { return pointer; }

Type* operator->() { return pointer; }

В частности, использование оператора -> из этого шаблона не должно требовать никаких дополнительных вычислений по сравнению со встроенными указателями. Как всегда, обращайте особое внимание на слова хороший и должно. В хорошей реализации описанные выше умные указатели не требуют никаких дополнительных расходов. По крайней мере, хуже пока не стало.

Применения

Умные указатели - существа на редкость полезные, и мы проведем немало времени, изучая их применение на практике. Для простых умных указателей, рассматриваемых в этой главе, находятся столь же простые, но мощные применения.

Разыменование значения NULL

Рассмотрим одну из вариаций на тему умных указателей: template <c1ass Type> class SPN { private:

Type* pointer; public:

SPN() : pointer(NULL) {}

SPN(Type* p) : pointer(p) {}

operator Type*() { return pointer; }

Type* operator->()

if (pointer == NULL) {

cerr << Dereferencing NULL! << endl; pointer = new Type;

return pointer;

При попытке вызвать оператор -> для указателя pointer , равного NULL, в поток stderr выводится сообщение об ошибке, после чего создается фиктивный объект и умный указатель переводится на него, чтобы программа могла хромать дальше.



Существует столько разных решений, сколько найдется программистов, достаточно глупых для попыток разыменования значения NULL. Вот лишь несколько из них.

Использование #indef

Если вас раздражают дополнительные вычисления, связанные с этой логикой, проще всего окружить if-блок директивами #ifdef, чтобы код обработки ошибок генерировался только в отладочных версиях программы. При компиляции рабочей версии перегруженный оператор -> снова сравнивается по быстродействию со встроенным указателем.

Инициирование исключений

Выдача сообщений об ошибках может вызвать проблемы в некоторых графических программах. Вместо этого можно инициировать исключение:

template <c1ass Type> class Ptr { private:

Type* pointer; public:

enum ErrorType { DereferenceNil };

Ptr() : pointer(NULL) {} Ptr(Type* p) : pointer(p) {} operator Type*() { return pointer; } Type* operator->() throw(ErrorType)

if (pointer == NULL) throw DereferenceNil; return pointer;

(На практике ErrorType заменяется глобальным типом, используемым для различных видов ошибок; приведенный фрагмент лишь демонстрирует общий принцип.) Это решение может объединяться с другими. Например, программа может использовать фиктивный объект в отладочном варианте и инициировать исключение в рабочей версии.

Стукачи

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

template <c1ass Type> class AHHH { private:

Type* pointer;

static type* screamer; public:

AHHH() : pointer(NULL) {} AHHH(Type* p) : pointer(p) {} Operator Type*() { return pointer; } Type* operator->()

if (p == NULL) return screamer; return pointer;



Ну и что такого? - спросите вы. Предположим, screamer на самом деле не принадлежит к типу Type* а относится к производному классу, все функции которого (предположительно виртуальные) выводят сообщения об ошибках в поток сеrr перед вызовом своих прототипов базового класса. Теперь вы не только удержите свою программу на плаву, но и сможете следить за попытками вызова функций фиктивного объекта.

Отладка и трассировка

Умные указатели также могут использоваться для наблюдения за объектами, на которые они указывают. Поскольку все обращения к объекту выполняются через операторную функцию operator Type*() или operator->() , у вас появляются две контрольные точки для наблюдения за происходящим во время работы программы. Возможности отладки безграничны, я приведу лишь один из примеров.

Установка точек прерывания

Самое простое применение упомянутых контрольных точек - сделать эти функции вынесенными (out-of-line) в отладочной версии и расставить точки прерывания в их реализации.

template <c1ass Type> class PTracer { private:

Type* pointer; public:

PTracer() : pointer(NULL) {} PTracer(Type* p) : pointer(p) {} operator Type*(); Type* operator->();

template <c1ass Type> #ifdef DEBUGGING inline #endif

PTracer<Type>::operator Type*()

return pointer; Здесь устанавливается точка прерывания

template <c1ass Type> #ifdef DEBUGGING inline

#endif

Type* PTracer<Type>::operator->()

return pointer; Здесь устанавливается точка прерывания

С непараметризованными версиями указателей это сделать несколько проще, поскольку не все среды разработки позволяют устанавливать точки прерывания в параметризованных функциях.

Трассировка

Оператор преобразования и оператор -> могут выводить диагностическую информацию в поток cout или cerr , в зависимости от ситуации.



1 ... 19 20 21 [ 22 ] 23 24 25 ... 82

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