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

时间:2011-08-17 18:32:27

标签: c++ refcounting

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

atomic<int> cntMyObject;

我 - 在析构函数中,++增加构造函数,cpy构造函数(我希望我涵盖了所有内容:))。 但这对每个班级来说都是硬编码。在“释放”模式下禁用它并不简单。 那么有没有简单优雅的方法可以轻松禁用来计算对象实例?

8 个答案:

答案 0 :(得分:27)

有一个“计数对象”类,在其构造函数和析构函数中执行正确的引用计数,然后派生您想要从中跟踪的对象。然后,您可以使用奇怪的重复模板模式为您想要跟踪的任何对象类型获取不同的计数。

// 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>
{};

答案 1 :(得分:9)

最好使用内存分析&amp;泄漏检测工具,如Valgrind或Rational Purify。

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

您应该为您的类重载newdelete运算符,然后在其中实现内存诊断。

请查看 this C ++常见问题解答,了解如何操作以及应采取的预防措施。

答案 2 :(得分:7)

您可以应用此方法

#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()方法将返回创建的对象的数量。

答案 3 :(得分:3)

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

它会覆盖operator new并执行一些神秘的宏内容,使其“标记”每个分配的信息,允许它计算一个对象的实例数和它们使用的内存量。但它并不完美,它们使用的宏在某些条件下会崩溃。如果您决定尝试这一点,请确保在任何标准标题之后包含它。

答案 4 :(得分:2)

在不知道您的代码和要求的情况下,我看到了两个合理的选择:

a)使用boost::shared_ptr。它具有您建议的内置原子引用计数并负责您的内存管理(这样您就不会真正关心计数)。其引用计数可通过use_count()成员获得。

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

答案 5 :(得分:1)

我们曾经有一个带有内部计数器的基类的解决方案,并从中派生出来,但是我们把它全部改成了boost :: shared_ptr,它保留了一个引用计数器,它为你清理了内存。 boost智能指针系列非常有用: boost smart pointers

答案 6 :(得分:1)

我的方法,将泄漏计数输出到调试输出(通过我们的代码库中实现的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>

答案 7 :(得分:1)

在某些答案中讨论了将计数器添加到各个类。但是,它需要选择要计数的类并以一种或另一种方式进行修改。在下面的假设中,您正在添加这样的计数器来查找使某些类的活动对象比预期更多的错误。

简要回顾一下已经提到的一些事情:对于实际内存泄漏,当然有valgrind:memcheck和泄漏清理程序。但是,对于其他没有真正泄漏的情况,它们无济于事(未清除的向量,从未访问过键的映射条目,shared_ptrs的周期等等)。

但是,由于没有提到这一点:在valgrind工具套件中,还有massif,它可以为您提供有关所有已分配内存及其分配位置的信息。但是,让我们假设valgrind:massif也不是您的选择,并且您确实希望实例计数。

出于偶然的错误查找的目的-如果您愿意采用某些骇人听闻的解决方案,并且上述选项不起作用-您可以考虑以下事项:如今,堆中的许多对象已由智能指针有效地保存。这可以是标准库中的智能指针类,也可以是您使用的各个帮助程序库的智能指针类。然后,技巧如下(以shared_ptr为例):通过修补shared_ptr实现,即通过将实例计数添加到shared_ptr类,可以一次获取许多类的实例计数器。然后,对于某些Foo类,属于shared_ptr 的计数器将为您指示Foo类的实例数量。

当然,它不如直接将计数器添加到各个类中那样准确(不计算仅由原始指针引用的实例),但是对于您的情况,它可能足够准确。而且,当然,这不是要永久更改智能指针类,而只是在寻找错误的过程中。至少,智能指针的实现不太复杂,因此对其进行修补很简单。