00:20 

Трюк 0x0007 — Узнаем, сколько создано объектов

Куб 0
В программе, изобилующей мелкими объектами, иногда необходимо выяснить, сколько именно объектов создано, сотня или же миллион. А если объекты создаются и уничтожаются постоянно в течение жизненного цикла программы, то зачастую интересует вопрос, сколько раз были созданы объекты того или иного типа?
Ключевые элементы такого счетчика объектов вырисовываются сразу: некие глобальные переменные и привязка к типам объектов.
Сразу хочется сказать о подводных камнях, которые обнаруживаются в процессе организации счетчика. Дело в том, что классы как правило имеют наследование, поэтому простое инкрементирование счетчика в каждом конструкторе никуда не годится — при вызове конструктора будут инкрементированы и счетчики всех базовых классов.
В приведенной далее реализации счетчика объектов используется соглашение, что базовый класс отмечается разработчиком. Для различения счетчиков разных объектов используется шаблонный класс, который специфицируется типом. (Возможно, альтернативным более компактным решением было бы специфицирование шаблона строкой, получаемой из typeid при подстановке this.) Счетчик, установленный в каком либо классе, обязательно должен быть установлен и во всех его производных классах, если только Вы не хотите, чтобы объекты производных классов подсчитывались в одной куче с объектами базовых классов.

//------------------------------------------------------------
#include <memory>
class InstanceCounterBase {
public:
virtual void SubOne()=0;
static std::auto_ptr<InstanceCounterBase> m_PrevCounter;
};
std::auto_ptr<InstanceCounterBase> InstanceCounterBase::m_PrevCounter;
//------------------------------------------------------------
template <class T>
class InstanceCounter: InstanceCounterBase {
private:
InstanceCounter() { ++m_Counter; }
void SubOne() { --m_Counter; }
public:
static int Value() { return m_Counter; }
static void Install(bool Base=false) {
if (Base) { m_PrevCounter.release(); }
if (m_PrevCounter.get()) { m_PrevCounter->SubOne(); }
m_PrevCounter.reset(new InstanceCounter<T>());
}
private:
static int m_Counter;
};
template <class T> int InstanceCounter<T>::m_Counter=0;
//------------------------------------------------------------


Счетчик устанавливается на объект класса в конструкторе вызовом метода Install. При установке счетчика на базовый класс необходимо вызвать метод Install с параметром true, что является для счетчика признаком базового класса.

//------------------------------------------------------------
class Base {
public:
Base() {
InstanceCounter<Base>::Install(true); // пометка базового класса
}
};
class Derived: virtual public Base {
public:
Derived(): Base() {
InstanceCounter<Derived>::Install();
}
};
class Derived2: virtual public Base {
public:
Derived2(): Base() {
InstanceCounter<Derived2>::Install();
}
};
class Derived2_2: public Derived2 {
public:
Derived2_2(): Derived2() {
InstanceCounter<Derived2_2>::Install();
}
};
class SuperDerived: public Derived, public Derived2 {
public:
SuperDerived(): Derived(), Derived2() {
InstanceCounter<SuperDerived>::Install();
}
};
class Another {
public:
Another() {
InstanceCounter<Another>::Install(true); // другой базовый класс
}
};
//------------------------------------------------------------
#include <iostream>
int main() {
Derived D1, D2, D3;
Base B1, B2;
Another A1, A2;
Derived2_2 D2_21;
SuperDerived SD1;
std::cout << "Base #" << InstanceCounter<Base>::Value() << std::endl;
std::cout << "Derived #" << InstanceCounter<Derived>::Value() << std::endl;
std::cout << "Derived2 #" << InstanceCounter<Derived2>::Value() << std::endl;
std::cout << "Derived2_2 #" << InstanceCounter<Derived2_2>::Value() << std::endl;
std::cout << "SuperDerived #" << InstanceCounter<SuperDerived>::Value() << std::endl;
std::cout << "Another #" << InstanceCounter<Another>::Value() << std::endl;
return 0;
}
//------------------------------------------------------------
В примере создана некоторая иерархия классов, создано по несколько объектов каждого класса и результат подсчета количества объектов выведен на экран.

//------------------------------------------------------------
Base #2
Derived #3
Derived2 #0
Derived2_2 #1
SuperDerived #1
Another #2
//------------------------------------------------------------
Все правильно, и при обычном наследовании счетчик работает корректно. Счетчик корректно работает и при виртуальном наследовании. Для множественного наследования такой счетчик работает при условии, что иерархия классов имеет один базовый класс. В общем случае, для множественного наследования нужно искать другое решение, либо вовсе не устанавливать такой счетчик в базовые классы.

Thread-safe оптимизированный вариант счетчика, предложенный моим коллегой, сопряжен с использованием множественного наследования, и содержит три класса. Однако прост для понимания, и категорически рекомендован к использованию.
Читать дальше...

@темы: Счётчики объектов, Трюки с наследованием

URL
Комментарии
2008-03-21 в 18:57 

Malinovski


А теперь умри силою моего гения!!! Я тебя щас повергну в смертельный шок и растопчу в пыль и прах твой жалкий труп следующим своим заклинанием:




/**
* @class Counter
* Базовый класс иерархии классов, объекты которых будут подсчитываться.
* Класс Counter содержит указатель InstanceCounterBase * m_PrevCounter
* - указатель на предыдущий счётчик, таким образом он будет присутствовать во всех
* пораждённых классах в иерархии. В каждом конструкторе необходимо вставить
* строчку "Install<тип класса>();". Во время создания объекта класса, в порождённых
* классах будет декрементироваться счётчик базового или предыдущего класса,
* указателю m_PrevCounter присвается адрес собственного счётчика, после чего инкрементироваться
* счётчик собственного класса. Следовательно счётчик, который был увеличен базовым классом,
* будет уменьшен порождённым и инкрементированным останется только последний класс в иерархии.
* При разрушении объекта, когда дойдёт дело до деструктора класса Counter, то указатель
* m_PrevCounter будет содержать указатель на счётчик последнего класса иерархии
* и декрементирует его.
*/

class Counter {
template <class T> class CounterBase {
friend class Counter;
static int m_Count;
};
protected:
int * m_PrevCounter;
public:
Counter(void) {
CounterBase<Counter> CounterBaseObj;
m_PrevCounter = &(CounterBaseObj.m_Count);
(*m_PrevCounter)++;
}
~Counter(void) {
(*m_PrevCounter)--;
}
template <class T> void Install(void) {
CounterBase<T> CounterBaseObj;
(*m_PrevCounter)--;
m_PrevCounter = &(CounterBaseObj.m_Count);
(*m_PrevCounter)++;
}
template <class T> static int Count(void) {
CounterBase<T> CounterBaseObj;
return CounterBaseObj.m_Count;
}
};
template <class T> int Counter::CounterBase<T>::m_Count = 0;

/**
* Данный макрос предназначен для более компактной и понятной записи.
* @return - возвращает количество объектов класса type.
*/

#define COUNTOF(type) Counter::Count<type>()



Тут уж соглаСИСЬКА - реализация гениальна своей простотой!!!



2008-03-21 в 21:15 

Куб 0
Чортовы гении... У меня защита пятого уровня против вашего заклинания.
Казалось бы, подкопаться не к чему, но я подкопаюсь.
Подкопаться тут можно к трем вещам. Во-первых, нафиг столько раз создавать временный объект CounterBaseObj? На него любой уважающий себя (кроме борландовского) компилятор будет орать, мол, unreferenced local variable. Во-вторых, зачем делать friend'ом класс Counter, если CounterBase находится в секции private, и никто и никогда не доберется до поля m_Count извне — можно его сделать в public, а friend тут совершенно неуместен. И в-третьих, даже младшая группа ясельного сада знает, что префиксные операции быстрее постфиксных, потому что не создают временных объектов:)
Так что оставляю последнее слово за собой.
Пользуйтесь:)
class Counter {
template <class T> class CounterBase {
public:
static int m_Count;
};
int * m_PrevCounter;
public:
Counter(void): m_PrevCounter(&(++CounterBase<Counter>::m_Count)) {}
~Counter(void) {
--(*m_PrevCounter);
}
template <class T> void Install(void) {
--(*m_PrevCounter);
m_PrevCounter = &(++CounterBase<T>::m_Count);
}
template <class T> static int Count(void) {
return CounterBase<T>::m_Count;
}
};
template <class T> int Counter::CounterBase<T>::m_Count = 0;

URL
2008-03-21 в 22:16 

Malinovski
Блин, даже спорить не буду, согласен на все 100%. Поторопился, идею реализовал, но до совершенства не довёл. Ну надо же было хоть немного и тебе дать возможность приложить руки и голову =))).
Вот теперь в таком виде это почти божественно!!!

2008-03-22 в 15:35 

Куб 0
Вот теперь в таком виде это почти божественно!!!

:beer:

URL
2008-05-30 в 01:26 

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

2008-08-26 в 23:42 

Куб 0
К сожалению, хорошие мысли приходят нечасто.
Да и по большей части все трюки описаны в специальной литературке.

URL
   

Записная книжка программиста C++

главная