10

在 C++ 程序中,对于一个类,我们如何获得在任何时间点的活动对象数量的计数,这些对象是静态创建和动态创建的?

4

5 回答 5

9

可悲的是你不能。在 Scott Meyer 的一本书中有一个完整的部分,他继续讲述试图实现这一目标所面临的挑战,但总而言之,这是不可能的。

更有效的 C++ - Item #27:要求或禁止基于堆的对象。

好的,这是很容易证明的问题之一(有问题的项目有几页长,所以我不会总结所有内容,但这里至少有一个挑战):

许多(但不是全部)系统以下列方式排列它们的内存:

----------------
|     Stack    |
| (Grows Down) |
|              |
----------------
|              |
|              |
|              |
|              |
|              |
|              |
----------------
|     Heap     |
|  (Grows Up)  |
|              |
----------------

现在您可能会认为,通过这样的内存安排,您可以使用 operator new/new 运算符做一些聪明的事情来确定您是否在堆上(通过检查您是在某个内存位置之上还是之下)?这就是问题所在。静态对象的去向取决于系统,因此可能会发生以下情况:

----------------
|     Stack    |
| (Grows Down) |
|              |
----------------
|              |
|              |
|              |
|              |
|              |
|              |
----------------
|     Heap     |
|  (Grows Up)  |
|              |
----------------
|    Static    | 
|    Objects   |
----------------

您现在无法区分静态对象和堆对象。哎呀!另外你可能已经注意到我说这是系统依赖的,这意味着即使你想办法区分它们,你的代码也不会是可移植的。

于 2013-07-23T15:12:47.157 回答
2

警告:这使用了“未定义的行为”,如下所述 - 它可以在大多数平台上工作(我有足够的理解说这适用于 Windows、Linux 和 Symbian 操作系统中的 ARM 和 x86,并且对于大多数操作系统来说应该没问题使用“平面”内存模型)。

如果您将自己“限制”到特定系统,则可以与this已知范围的“堆栈在哪里”(如果需要)进行比较,静态数据在哪里)。[也可以确定任意线程的堆栈在哪里,但这使挑战变得更加困难]。

有了静态数据和堆栈所在位置的知识,我们可以比较

char *this_addr = static_cast<char*>(this);
if (this_addr >= globa_start && this_addr <= global_end) 
   globals++;
else if (this_addr >= stacK_top && this_addr >= stack_base)
   stacked++;
else heaped++; 

请注意,这只有在您实际上能够以某种方式确定堆栈的位置时才有效 - 当然,this与分配它的块之外的任何内容进行比较是未定义的行为,因此从技术上讲,整个概念是未定义的。但是,在大多数操作系统/处理器架构中都可以这样做。[当然,你也需要在析构函数中做同样的事情,但反过来]。(如果您从与创建它的线程不同的线程销毁对象,它也会变得“有趣”!)

编辑:查找给定线程的堆栈并不难:存储[每个线程,如果有多个线程]“第一个函数”中的局部变量的地址(传递给线程创建调用的那个)。然后在当前线程中取一个变量的地址。这些值之间的任何内容都在该线程堆栈中,因为堆栈是一个连续的内存块。

于 2013-07-23T15:26:05.447 回答
1

您可以通过传递有关其位置的参数来简单地告诉该类:

class LocationAware {
public:
    enum Location { STATIC, STACK, HEAP };
    explicit LocationAware(Location location) : my_location(location) {
        switch(location) {
            case STATIC: ++static_instaces; break;
            case STACK: ++stack_instances; break;
            case HEAP: ++heap_instances; break;
        }
    }

    ~LocationAware() {
        switch(my_location) {
            case STATIC: --static_instaces; break;
            case STACK: --stack_instances; break;
            case HEAP: --heap_instances; break;
        }
    }

private:
    const Location my_location;

public:
    static unsigned static_instaces;
    static unsigned heap_instances;
    static unsigned stack_instances;
};

unsigned LocationAware::static_instaces = 0;
unsigned LocationAware::heap_instances = 0;
unsigned LocationAware::stack_instances = 0;

LocationAware stat(LocationAware::STATIC);

int main() {
    LocationAware stack(LocationAware::STACK);

    LocationAware * heap = new LocationAware(LocationAware::HEAP);
}

当然,你可以对这个班级撒谎。不。

此外,如果您想让它不那么具有侵入性,您可以将其设为模板并使用继承或封装并从您的类中使用它。只需给它一个参数:

template<class Tag>
LocationAware;

然后在你的类中继承或保存一个位置并初始化它。您可以使用 . 查看实例LocationAware<YourClassOrTag>::xy_instances

于 2013-07-23T15:56:55.910 回答
0

跟踪活动对象数量的最简单的解决方案是创建一个对象管理器(使用 GetSize() 函数或其他)

在您要跟踪的类中,您还可以添加一个静态属性,该属性将分别在构造函数和析构函数中增加和减少。

使用对象管理器的大小(动态分配)静态属性(所有分配),您将能够分别检索这些数字。

于 2013-07-23T15:26:15.887 回答
0

作为一种选择,您可以全局重载 new 和 delete 以增加/减少一些静态计数器,这将给出动态分配对象的全局计数......

于 2013-07-23T15:35:21.970 回答