|
Отладка и трассировка Умные указатели также могут использоваться для наблюдения за объектами, на которые они указывают. Поскольку все обращения к объекту выполняются через операторную функцию operator Type*() или operator->(), у вас появляются две контрольные точки для наблюдения за происходящим во время работы программы. Возможности отладки безграничны, я приведу лишь один из примеров. Установка точек прерывания Самое простое применение упомянутых контрольных точек - сделать эти функции вынесенными (outof-line) в отладочной версии и расставить точки прерывания в их реализации. template <class Type> class PTracer { private: Type* pointer; public: PTracer() : pointer(NULL) {} PTracer(Type* p) : pointer(p) {} operator Type*(); Type* operator->(); }; template <class Type> #ifdef DEBUGGING inline #endif PTracer<Type>::operator Type*() { return pointer; // Здесь устанавливается точка прерывания } template <class Type> #ifdef DEBUGGING inline #endif Type* PTracer<Type>::operator->() { return pointer; // Здесь устанавливается точка прерывания } С непараметризованными версиями указателей это сделать несколько проще, поскольку не все среды разработки позволяют устанавливать точки прерывания в параметризованных функциях. Трассировка Оператор преобразования и оператор -> могут выводить диагностическую информацию в поток cout или cerr, в зависимости от ситуации. Ведение статистики класса Также несложно организовать накопление статистики об использовании операторов Type* и -> в статических переменных параметризованного класса. template <class Type> class SPCS { private: Type* pointer; static int conversions; static int members; public: SPCS() : pointer(NULL) {} SPCS(Type* p) : pointer(p) {} operator Type*() { conversions++; return pointer; } Type* operator->() { members++; return pointer; } int Conversions() { return conversions; } int Members() { return members; } }; Глобальные переменные должны быть где-то определены. Обычно это делается в файле Foo.cpp: // В файле Foo.cpp int Ptr<Foo>::conversions = 0; int Ptr<Foo>::members = 0; Разумеется, вы можете воспользоваться директивами #ifdef, чтобы это относилось только к отладочной версии. Ведение статистики объекта Мы подошли к более сложной теме. Возможно, ее следует отложить до знакомства с ведущими указателями (master pointers), однако умные указатели также могут вести статистику по отдельным объектам, а не по классу в целом. Задача не сводится к тому, чтобы сделать только что показанные переменные нестатическими (то есть по одному экземпляру переменных на указатель), поскольку мы (пока) не можем обеспечить однозначное соответствие между указателями и объектами. Вместо этого статистику придется хранить в самих объектах. Ниже приведен полезный вспомогательный класс, который можно создать на основе множественного наследования как производный от класса указываемого объекта и от класса умного указателя, знающего о его свойствах. Объявляя указатель другом, вы предоставляете ему доступ к защищенным членам классов, производных от Counter. class Counter { protected: Counter() : conversions(0), members(0) {} Counter(const Counter&) : conversions(0), members(0) {} Counter& operator=(const Counter&) { return *this; } public: int conversions; int members; int Conversions() { return conversions; } int Members() { return members; } }; template <class Type> class SPOP { private: Type* pointer; public: SPOS() : pointer(NULL) {} SPOP(Type* f) : pointer(f) {} operator Type*() { pointer->conversions++; return pointer; } Type* operator->() { pointer->members++; return pointer; } }; На эту тему существует ряд вариаций, с некоторыми из них мы познакомимся при изучении ведущих указателей. |
Copyright 2005. Климов Александр. All Right Reserved.