对某类对象计数有时是很有必要的, 尤其是利于调试. 因为一些内存泄露的 bug 的表象就是某些对象不停的被分配而没有释放. 对象的数目在短期膨胀到一个极大的值. 通常我们会写这样一个 template 来干这种事情:
template <typename T> class counter { public: counter() { ++_count; } ~counter() { --_count; } int current_count() const { return _count; } private: static int _count; }; template<typename T> int counter<T>::_count=0;
在需要记数的 class 里放一个 counter 成员就够了 :) 例如:
class foo { counter<foo> _counter; // ... };
然后任何时候都可以用 _counter.current_count() 取到当前对象的数目.
在完成其它功能(比如记录下高峰时对象最多的数目)前, 重新审视一下这个 template, 它有 bug 吗? 真的在任何合法的情况下都能正常工作吗? _count 有可能被减为负数吗?
回答是, 隐患是存在的! 这就是这篇文章想讨论的有趣问题. 因为 counter 没有写 copy ctor. 如果包含它的类也没有写 copy ctor, 编译器会自动产生一个. 但是, 由于 counter 本身是没有非静态成员变量的, 属于一个POD 类型. 所以它的 copy ctor 什么都不会做. 当然也不会 ++_count 了.
你可以试一下以下代码:
class foo { public: counter<foo> _counter; }; void main() { foo f1; foo f2(f1); printf("%d",f2._counter.current_count()); }
答案是 1 还是 2 ?
当然如果 class foo 有自己的 copy ctor, 答案又成了正确的 2. 因为在调用 foo 的 copy ctor 之前, 会调用 counter 下面随便写一些有趣但不复杂的代码, 完善这个 counter, 可以使每个 counter 都把自己注册到 counter_log 中方便检阅:
class counter_data;
class counter_log {
public:
static counter_log& instance() {
static counter_log __inst;
return __inst;
}
void add(const char *name,counter_data *cd) {
log_[name]=cd;
}
~counter_log() {}
void output() const; // 这个函数的实现取决于实际的运用.
private:
counter_log() {}
std::map<const char *,counter_data *> _log;
};
class counter_data {
int _counter;
int _max;
public:
counter_data(const char *name=0) {
counter_log::instance().add(name,this);
}
~counter_data() {}
void inc() {
if (++_counter>_max) {
_max=_counter;
}
}
void dec() {
--_counter;
}
int current_count() const { return _counter; }
int max_count() const { return _max; }
};
template <typename T>
class counter {
public:
counter() { _data.inc(); }
~counter() { _data.dec(); }
counter(const counter &) { _data.inc(); }
int current_count() const { return _data.current_count(); }
int max_count() const { return _data.max_count(); }
private:
static counter_data _data;
};
template<typename T> counter_data counter<T>::_data=counter_data(typeid(T).name());