|
Разыменование значения NULL Рассмотрим одну из вариаций на тему умных указателей: template <class 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 <class 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 <class 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 перед вызовом своих прототипов базового класса. Теперь вы не только удержите свою программу на плаву, но и сможете следить за попытками вызова функций фиктивного объекта. |
Copyright 2005. Климов Александр. All Right Reserved.