Материалы книги получены с http://www.itlibitum.ru/
Отладка и трассировка
Умные указатели также могут использоваться для наблюдения за объектами, на которые они указывают. Поскольку все обращения к объекту выполняются через операторную функцию 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; }
};
На эту тему существует ряд вариаций, с некоторыми из них мы познакомимся при изучении ведущих указателей.
Назад Содержание Далее
|