C++动态对象.如何在运行时确定对象大小?

C++ dynamic objects. How is object size determined during runtime?

本文关键字:对象 动态 C++ 运行时      更新时间:2023-10-16

我不明白一件事。例如,我声明类 A 和类 B 是 A 的子级:

class A {
public:
int a;
}
class B : public A {
public:
int b;
}

显然,如果我创建 A 或 B 的实例,它们在内存中的大小可以通过类型确定。

A instanceA; // size of this will probably be the size of int (property a)
B instanceB; // size of this will probably be twice the size of int (properties a and b)

但是,如果我创建动态实例,然后稍后释放它们,该怎么办?

A * instanceAPointer = new A();
A * instanceBPointer = new B();

这些是不同类的实例,但程序会将它们视为类 A 的实例。使用它们时这很好,但是释放它们呢?要释放分配的内存,程序必须知道要释放的内存大小,对吗?

所以如果我写

delete instanceAPointer;
delete isntanceBPointer;

程序如何知道,从每个指针指向的地址开始,它应该释放多少内存?因为显然对象具有不同的大小,但程序认为它们是A型。

谢谢

我假设你知道删除是如何工作的。

至于delete知道如何清理继承的实例。这就是在继承上下文中使用virtual析构函数的原因,否则将具有未定义的行为。基本上,析构函数与其他所有virtual函数一样,是通过vtable调用的。

还要回想一下:C++编译器隐式破坏析构函数中的父类

class A {
public:
int a;
virtual ~A(){}
}
class B : public A {
public:
int b;
~B() { /* The compiler will call ~A() at the very end of this scope */ }
}

这就是为什么这将起作用;

A* a = new B();
delete a;

通过vtable,析构函数~B()将由delete调用。由于编译器在派生类中隐式插入基类的析构函数调用,因此将在~B()中调用A的析构函数。

如果通过指向基子对象的指针删除对象,并且子对象的类没有虚拟析构函数,则行为是未定义的。

另一方面,如果它确实具有虚拟析构函数,则虚拟调度机制负责为正确的地址(即完整的、派生最多的对象)释放正确的内存量。

您可以通过将dynamic_cast<void*>应用于任何适当的基子对象指针来自己发现派生最多的对象的地址。(另请参阅此问题。

要释放分配的内存,程序必须知道要释放的内存大小,对吧?

如果你考虑C库mallocfree,你会发现调用free时不需要指定要释放的内存量,即使在这种情况下free提供了void*所以没有办法推断它。 相反,分配库通常要么记录,要么可以推断出足够的内存,以便仅指针就足以执行释放。

对于C++释放例程来说,这仍然是正确的:如果基类提供了自己的static void operator delete(void*, std::size_t)并且基类析构函数virtual,那么它将被传递动态类型的大小。 默认情况下,释放最终会达到不会被赋予任何大小的::operator delete(void*):分配例程本身必须有足够的知识才能运行。

分配例程的工作方式有多种,包括:

  • 存储分配的大小

  • 从相同大小的块池中分配大小相似的对象,以便进入该池的任何指针都隐式与该块大小相关