Предполагается, что каждый объект в программе при создании получает имя: ИМЯКЛАССА_N, где N номер объекта. В классе написал счетчик
class A
{
LPSTR m_lpName;
static UINT m_uCounter;
...
}
и в начале инициализирую счетчик нулем.
В конструкторе увеличиваю счетчик на единицу и создаю имя название объекта в m_lpName. Это все работает хорошо.
Проблемы начинаются, если происходит наследование
class В: public A
ведь статическую переменную нельзя проинициировать нулем снова. Поэтому нумерация экземпляров класа В продолжается с номера последнего объекта типа А. Что делать?
Здравствуйте, matrixman, Вы писали:
M>ведь статическую переменную нельзя проинициировать нулем снова. Поэтому нумерация экземпляров класа В продолжается с номера последнего объекта типа А. Что делать?
template<class T>
class Counter{
static int count;
protected:
Counter(){count++;};
Counter(const Counter & obj){count++;};
~Counter(){count--;};
public:
static int Count(){return count;}
};
template<class T>
int Counter<T>::count = 0;
вот для твоих классов:
class A : public Counter<A>
class B : public A, public Counter<B>
Здравствуйте, matrixman, Вы писали:
M>ведь статическую переменную нельзя проинициировать нулем снова. Поэтому нумерация экземпляров класа В продолжается с номера последнего объекта типа А. Что делать?
template <class T>
struct instances
{
unsigned value;
}
class A
{
const int _instance;
public:
A() : _instance (++instances<A>::value)
{
/// ....
}
~A() { --instances<A>::value; }
};
class B : public A
{
const int _instance;
public:
B() : _instance (++instances<B>::value)
{
/// ....
}
~B() { --instances<B>::value; }
};
// в .cpp
instances<A>::value = 0;
instances<B>::value = 0;
Здравствуйте, ssm, Вы писали:
ssm>Здравствуйте, matrixman, Вы писали:
ssm> M>>Еще один вопрос. Но вот тогда получается что класс В будет содержать методы и класса Counter<A>?
ssm>сделай закрытое наследование
Здравствуйте, matrixman, Вы писали:
M>Предполагается, что каждый объект в программе при создании получает имя: ИМЯКЛАССА_N, где N номер объекта. В классе написал счетчик
Если нужно считать ориентируясь только лишь на названия классов, то имеет смысл "набор счетчиков" вынести отдельно, получая доступ к соответсвующему счетчику по имени класса.
Здравствуйте, matrixman, Вы писали:
M>Предполагается, что каждый объект в программе при создании получает имя: ИМЯКЛАССА_N, где N номер объекта.
<>
Народ уже откликнулся, пока я писал... Тем не менее.
Варианты решения:
1) RTTI для определения имени класса данного объекта; глобальная динамическая таблица имя_класса -> число
typedef unsigned long serial_t;
class CSerialNumberTable
{
private:
typedef std::map< std::string, serial_t > table_t;
table_t table_;
public:
serial_t next(const char* s) { return ++(table_[s]); }
serial_t next(std::string const& s) { return ++(table_[s]); }
serial_t next(type_info const& t) { return next(t.name()); }
template< class T >
serial_t next_obj(T const& t) { return next(typeid(t)); }
template< class T >
serial_t next_ptr(T const* p) { return next(*t); }
template< class T >
serial_t next_type(T* = 0) { return next(typeid(T const))); }
};
2) шаблон счетчика
typedef unsigned long serial_t;
template< class T >
class CSerialNumber
{
public:
typedef T Base; // пригодится :)private:
static serial_t next()
{
static serial_t next_ = 0;
// заначим статическую переменную здесь
// - чтобы не определять за пределами шаблонного классаreturn ++next_;
}
serial_t serial_;
public:
CSerialNumber() : serial_(next()) {}
CSerialNumber(CSerialNumber const& src) : serial_(next()) {}
// сквозная нумерация. никаких копий!void operator =(CSerialNumber const& src) {}
// ничего не делать
serial_t serial() const { return serial_; }
};
Каждый объект каждого считаемого класса должен или наследоваться от счетчика, или содержать его.
При этом, в случае наследования, наблюдается очень интересное явление: один и тот же объект может сосчитаться в разных "каталогах".
В общем-то, это правильно...
class A : public CCountedBase
{
......
public:
A() : CCountedBase("A") .....; // явно указываем классprotected:
explicit A(const char* cn) : CCountedBase(cn) ......; // передаем имя класса "сверху"
};
class B : public A
{
......
public:
B() : A("B") .....; // явно указываем классprotected:
explicit B(const char* cn) : A(cn) .....; // передаем имя класса "сверху вниз"
};
В общем, стоит ли так мучаться — не знаю.
3) Если нужно подсчитать объекты не в момент их конструирования, а позже (и не обязательно все), то сделаем хитрее:
class CObjectBase;
class CObjectTable
{
private:
typedef std::map<CObjectBase const*, serial_t> obj_table_t;
obj_table_t objects_;
CSerialNumberTable numbers_;
public:
serial_t index(CObjectBase const* ptr)
{
obj_table_t::iterator it = objects_.find(ptr);
if(it == objects_.end())
objects_[ptr] = numbers_.next_ptr(ptr);
// можно сделать через insert - но так понятнееreturn objects_[ptr];
}
void reset(CObjectBase const* ptr)
{
objects_.erase(ptr);
}
};
class CObjectBase
{
static CObjectTable table_;
public:
virtual ~CObjectBase() { table_.reset(this); }
serial_t serial() const { return table_.index(this); } // инициализируется по первому запросу
};
На самом деле, важно понимать, что все предложенные способы нерешают твоей задачи в полной мере, т.к. предоставляют информацию о создании/разрушении подобъекта твоего объекта(неважно реализовано это делегированием или закрытым наследованием) а не самого объекта. Очень часто этого оказываеться вполне достаточно, но невсегда...
Проблема заключаеться в возможной генерации исключения в теле твоего конструктора при уже созданом подъобъекте
class A : private Counter<A>{
public:
A(){
//1throw"ups!";
}
};
...
int main(){
try{
A a;
}
catch(...){
//2
}
}
в точке 1 мы имеем один созданый Counter<A>, но ноль A
в точке 2 мы имеем один созданый Counter<A> и один удаленный Counter<A>, но ноль A