最简单的计算对象实例的方法

Simplest way to count instances of an object

本文关键字:方法 实例 计算 最简单 对象      更新时间:2023-10-16

我想知道在某个执行点分配的某些对象的确切实例数。主要是为了寻找可能的内存泄漏(我主要使用RAII,几乎没有新的,但我仍然可以在添加新元素或类似的东西之前忘记vector上的.clear())。我可以有一个

atomic<int> cntMyObject;

I——in destructor, ++ in constructor, cpy constructor(我希望我涵盖了所有内容:))。但这是对每个类的硬编码。在"释放"模式下禁用它并不简单。那么,是否有一种简单优雅的方法可以很容易地禁用来计数对象实例呢?

有一个'计数对象';类在其构造函数和析构函数中执行正确的引用计数,然后从中派生要跟踪的对象。然后,您可以使用奇怪的重复出现的模板模式来获得您希望跟踪的任何对象类型的不同计数。

// warning: pseudo code
template <class Obj>
class CountedObj
{
public:
   CountedObj() {++total_;}
   CountedObj(const CountedObj& 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。

如果你不能并且想要实现你自己的机制,那么

您应该为您的类重载newdelete操作符,然后在它们中实现内存诊断。

请查看这个 c++常见问题解答,了解如何做到这一点以及应该采取哪些预防措施。

这是一个类似的工作示例:http://www.almostinfinite.com/memtrack.html(只需复制页面末尾的代码并将其放入Memtrack.h中,然后运行TrackListMemoryUsage()或其他函数之一以查看诊断)

它覆盖了操作符new,并做了一些神秘的宏的东西,使它'stamp'每个分配的信息,允许它计算有多少个对象的实例和它们使用了多少内存。但这并不完美,他们使用的宏在某些条件下会崩溃。如果您决定尝试一下,请确保将其包含在任何标准头文件之后。

在不了解您的代码和需求的情况下,我认为有两个合理的选择:

a)使用boost::shared_ptr。它内置了您建议的原子引用计数,并负责内存管理(因此您实际上永远不会关心计数)。它的引用计数可以通过use_count()成员获得。

b)如果a)的含义,比如处理指针和到处都有shared_ptrs,或者可能的性能开销,对你来说是不可接受的,我建议简单地使用可用的内存泄漏检测工具(例如Valgrind,见上文),它将在程序退出时报告您的松散对象。而且没有必要使用侵入性的助手类来跟踪对象计数(无论如何仅用于调试),这只会弄乱您的代码。

我们曾经有一个带有内部计数器的基类的解决方案,并从它派生,但我们将其全部更改为boost::shared_ptr,它保留一个引用计数器并为您清理内存。boost智能指针系列非常有用:boost智能指针

我的方法,它将泄漏计数输出到调试输出(通过在我们的代码库中实现的DebugPrint函数,用您自己的调用替换该调用…)

#include <typeinfo> 
#include <string.h>
class CountedObjImpl
{
public:
        CountedObjImpl(const char* className) : mClassName(className) {}
        ~CountedObjImpl()
        {
                DebugPrint(_T("**##** Leakage count for %hs: %Iun"), 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也不是您的选项,并且您确实需要实例计数。

为了偶尔寻找bug——如果你想要一些hack的解决方案,如果上面的选项不起作用——你可以考虑如下:现在,堆上的许多对象都有效地由智能指针保存。这可以是标准库中的智能指针类,也可以是您使用的各个辅助库的智能指针类。诀窍如下(以shared_ptr为例):您可以通过修补shared_ptr实现,即通过向shared_ptr类添加实例计数,一次获得许多类的实例计数器。然后,对于某个类Foo,属于shared_ptr将显示类Foo的实例数量。

当然,它不如直接将计数器添加到相应的类中那么准确(仅由原始指针引用的实例不计算在内),但对于您的情况,它可能足够准确。当然,这并不是要永久地改变智能指针类——只是在寻找bug的过程中。至少,智能指针的实现不是太复杂,所以修补它们很简单。

这种方法比这里的其他解决方案简单得多。

为计数创建一个变量,并将其设置为静态。该变量在构造函数中加1,在析构函数中减-1。

确保你初始化了变量(它不能在头文件中初始化,因为它是静态的)。

. h

// Pseudo code warning
class MyObject
{
   MyObject();
   ~MyObject();
   static int totalObjects;
}
<<p> . cpp/strong>
int MyObject::totalObjects = 0;
MyObject::MyObject()
{
   ++totalObjects;
}
MyObject::~MyObject()
{
   --totalObjects;
}

对于你创建的每一个新实例,构造函数被调用,totalObjects自动增加1