在 C++ 程序中,对于一个类,我们如何获得在任何时间点的活动对象数量的计数,这些对象是静态创建和动态创建的?
5 回答
可悲的是你不能。在 Scott Meyer 的一本书中有一个完整的部分,他继续讲述试图实现这一目标所面临的挑战,但总而言之,这是不可能的。
更有效的 C++ - Item #27:要求或禁止基于堆的对象。
好的,这是很容易证明的问题之一(有问题的项目有几页长,所以我不会总结所有内容,但这里至少有一个挑战):
许多(但不是全部)系统以下列方式排列它们的内存:
----------------
| Stack |
| (Grows Down) |
| |
----------------
| |
| |
| |
| |
| |
| |
----------------
| Heap |
| (Grows Up) |
| |
----------------
现在您可能会认为,通过这样的内存安排,您可以使用 operator new/new 运算符做一些聪明的事情来确定您是否在堆上(通过检查您是在某个内存位置之上还是之下)?这就是问题所在。静态对象的去向取决于系统,因此可能会发生以下情况:
----------------
| Stack |
| (Grows Down) |
| |
----------------
| |
| |
| |
| |
| |
| |
----------------
| Heap |
| (Grows Up) |
| |
----------------
| Static |
| Objects |
----------------
您现在无法区分静态对象和堆对象。哎呀!另外你可能已经注意到我说这是系统依赖的,这意味着即使你想办法区分它们,你的代码也不会是可移植的。
警告:这使用了“未定义的行为”,如下所述 - 它可以在大多数平台上工作(我有足够的理解说这适用于 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
与分配它的块之外的任何内容进行比较是未定义的行为,因此从技术上讲,整个概念是未定义的。但是,在大多数操作系统/处理器架构中都可以这样做。[当然,你也需要在析构函数中做同样的事情,但反过来]。(如果您从与创建它的线程不同的线程销毁对象,它也会变得“有趣”!)
编辑:查找给定线程的堆栈并不难:存储[每个线程,如果有多个线程]“第一个函数”中的局部变量的地址(传递给线程创建调用的那个)。然后在当前线程中取一个变量的地址。这些值之间的任何内容都在该线程堆栈中,因为堆栈是一个连续的内存块。
您可以通过传递有关其位置的参数来简单地告诉该类:
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
。
跟踪活动对象数量的最简单的解决方案是创建一个对象管理器(使用 GetSize() 函数或其他)
在您要跟踪的类中,您还可以添加一个静态属性,该属性将分别在构造函数和析构函数中增加和减少。
使用对象管理器的大小(动态分配)和静态属性(所有分配),您将能够分别检索这些数字。
作为一种选择,您可以全局重载 new 和 delete 以增加/减少一些静态计数器,这将给出动态分配对象的全局计数......