我想知道在某个执行点分配的某些对象的确切实例数。主要是为了寻找可能的内存泄漏(我主要使用 RAII,几乎没有新的,但在添加新元素或类似的东西之前我仍然可以忘记向量上的 .clear())。Ofc 我可以有一个
atomic<int> cntMyObject;
我 - 在析构函数中,++ 增加构造函数,cpy 构造函数(我希望我涵盖了所有内容:))。但这是每个班级的硬编码。在“发布”模式下禁用它并不简单。那么有没有什么简单优雅的方法可以很容易地禁用来计算对象实例呢?
我想知道在某个执行点分配的某些对象的确切实例数。主要是为了寻找可能的内存泄漏(我主要使用 RAII,几乎没有新的,但在添加新元素或类似的东西之前我仍然可以忘记向量上的 .clear())。Ofc 我可以有一个
atomic<int> cntMyObject;
我 - 在析构函数中,++ 增加构造函数,cpy 构造函数(我希望我涵盖了所有内容:))。但这是每个班级的硬编码。在“发布”模式下禁用它并不简单。那么有没有什么简单优雅的方法可以很容易地禁用来计算对象实例呢?
有一个“计数对象”类,该类在其构造函数和析构函数中进行正确的引用计数,然后派生要从中跟踪的对象。然后,您可以使用奇怪的重复模板模式来获取您希望跟踪的任何对象类型的不同计数。
// warning: pseudo code
template <class Obj>
class CountedObj
{
public:
CountedObj() {++total_;}
CountedObj(const CountedObj& obj) {if(this != &obj) ++total_;}
~CountedObj() {--total_;}
static size_t OustandingObjects() {return total_;}
private:
static size_t total_;
};
class MyClass : private CountedObj<MyClass>
{};
你可以应用这种方法
#ifdef DEBUG
class ObjectCount {
static int count;
protected:
ObjectCount() {
count++;
}
public:
void static showCount() {
cout << count;
}
};
int ObjectCount::count = 0;
class Employee : public ObjectCount {
#else
class Employee {
#endif
public:
Employee(){}
Employee(const Employee & emp) {
}
};
在DEBUG
模式下,调用ObjectCount::showCount()
方法将返回创建的对象的计数。
最好使用内存分析和泄漏检测工具,如 Valgrind 或 Rational Purify。
如果您不能并且想要实现自己的机制,那么,
您应该为您的类重载new
anddelete
运算符,然后在其中实现内存诊断。
看看这个C++ 常见问题解答以了解如何执行此操作以及应采取哪些预防措施。
这是一个类似的工作示例:http ://www.almostinfinite.com/memtrack.html (只需复制页面末尾的代码并将其放入 Memtrack.h,然后运行 TrackListMemoryUsage() 或查看诊断的其他功能之一)
它覆盖 operator new 并执行一些神秘的宏操作,以使其使用信息“标记”每个分配,这些信息允许它计算对象的实例数以及它们正在使用的内存量。虽然它并不完美,但他们使用的宏在某些条件下会发生故障。如果您决定尝试此操作,请确保将其包含在任何标准标题之后。
在不知道您的代码和您的要求的情况下,我看到了 2 个合理的选择:
一)使用boost::shared_ptr
。它具有您建议的内置原子引用计数,并负责您的内存管理(这样您就不会真正关心查看计数)。其引用计数可通过use_count()
成员获得。
b) 如果 a) 的含义,比如处理指针和shared_ptrs
无处不在,或者可能的性能开销,对你来说是不可接受的,我建议简单地使用可用的内存泄漏检测工具(例如Valgrind,见上文)将在程序退出时报告您的松散对象。并且没有必要使用侵入性帮助类来(无论如何只调试)跟踪对象计数,这只会弄乱你的代码,恕我直言。
我们曾经有一个带有内部计数器的基类的解决方案并从它派生,但我们将其全部更改为 boost::shared_ptr,它保留一个引用计数器并为您清理内存。boost智能指针家族非常有用: boost smart pointers
我的方法,将泄漏计数输出到调试输出(通过我们代码库中实现的 DebugPrint 函数,用您自己的调用替换该调用......)
#include <typeinfo>
#include <string.h>
class CountedObjImpl
{
public:
CountedObjImpl(const char* className) : mClassName(className) {}
~CountedObjImpl()
{
DebugPrint(_T("**##** Leakage count for %hs: %Iu\n"), mClassName.c_str(), mInstanceCount);
}
size_t& GetCounter()
{
return mInstanceCount;
}
private:
size_t mInstanceCount = 0;
std::string mClassName;
};
template <class Obj>
class CountedObj
{
public:
CountedObj() { GetCounter()++; }
CountedObj(const CountedObj& obj) { GetCounter()++; }
~CountedObj() { GetCounter()--; }
static size_t OustandingObjects() { return GetCounter(); }
private:
size_t& GetCounter()
{
static CountedObjImpl mCountedObjImpl(typeid(Obj).name());
return mCountedObjImpl.GetCounter();
}
};
示例用法:
class PostLoadInfoPostLoadCB : public PostLoadCallback, private CountedObj<PostLoadInfoPostLoadCB>
在一些答案中讨论了向单个类添加计数器。但是,它需要选择要计算的类并以一种或另一种方式修改它们。以下假设是,您正在添加此类计数器以查找某些类的更多对象保持活动状态而不是预期的错误。
简要回顾一下已经提到的一些事情:对于真正的内存泄漏,肯定有 valgrind:memcheck 和泄漏消毒剂。但是,对于没有真正泄漏的其他场景,它们无济于事(未清除的向量、从未访问过键的映射条目、shared_ptrs 的循环……)。
但是,由于没有提到这一点:在 valgrind 工具套件中还有 massif,它可以为您提供有关所有已分配内存的信息以及它们的分配位置。但是,让我们假设 valgrind:massif 也不适合您,并且您确实需要实例计数。
出于偶尔寻找错误的目的——如果你愿意接受一些骇人听闻的解决方案并且如果上述选项不起作用——你可能会考虑以下几点: 现在,堆上的许多对象都由智能指针有效地保存。这可能是标准库中的智能指针类,也可能是您使用的相应帮助库的智能指针类。诀窍如下(以 shared_ptr 为例):您可以通过修补 shared_ptr 实现,即通过将实例计数添加到 shared_ptr 类来一次获取许多类的实例计数器。然后,对于某些类 Foo,属于 shared_ptr<Foo> 的计数器将为您提供类 Foo 实例数的指示。
当然,它不如直接将计数器添加到相应的类中准确(仅由原始指针引用的实例不计算在内),但对于您的情况可能已经足够准确了。而且,当然,这不是关于永久更改智能指针类 - 仅在寻找错误期间。至少,智能指针的实现并不太复杂,因此修补它们很简单。