我想知道当给定一个与对象的真实内存位置不同的基类指针时,删除运算符如何计算出需要释放的内存位置。
我想在我自己的自定义分配器/释放器中复制此行为。
考虑以下层次结构:
struct A
{
unsigned a;
virtual ~A() { }
};
struct B
{
unsigned b;
virtual ~B() { }
};
struct C : public A, public B
{
unsigned c;
};
我想分配一个 C 类型的对象并通过 B 类型的指针删除它。据我所知,这是对 operator delete 的有效使用,它在 Linux/GCC 下工作:
C* c = new C;
B* b = c;
delete b;
有趣的是指针'b'和'c'实际上指向不同的地址,因为对象在内存中是如何布局的,而删除操作符“知道”如何找到和释放正确的内存位置。
我知道,一般来说,给定基类指针是不可能找到多态对象的大小的:Find out the size of a polymorphic object .我怀疑通常也无法找到对象的真实内存位置。
注意事项:
- 我的问题与 new[] 和 delete[] 的工作方式无关。我对单个对象分配案例感兴趣。 How does delete[] "know" the size of the operand array? .
- 我也不关心如何调用析构函数。我对内存本身的释放感兴趣。 How 'delete' works when I delete a pointer of base class
- 我使用 -fno-rtti 和 -fno-exceptions 进行了测试,因此 G++ 不应访问运行时类型信息。
最佳答案
这显然是特定于实现的。在实践中,实现事物的明智方法相对较少。从概念上讲,这里有一些问题:
您需要能够获得指向最派生对象的指针,即(从概念上)包含所有其他类型的对象。
在标准 C++ 中,您可以使用
dynamic_cast
:void *derrived = dynamic_cast<void*>(some_ptr);
从
B*
中获取C*
,例如:#include <iostream> struct A { unsigned a; virtual ~A() { } }; struct B { unsigned b; virtual ~B() { } }; struct C : public A, public B { unsigned c; }; int main() { C* c = new C; std::cout << static_cast<void*>(c) << "\n"; B* b = c; std::cout << static_cast<void*>(b) << "\n"; std::cout << dynamic_cast<void*>(b) << "\n"; delete b; }
在我的系统上提供以下内容
0x912c008 0x912c010 0x912c008
Once that's done it then becomes a standard memory allocation tracking problem. Usually that's done in one of two ways, either a) record the size of the allocation just before the allocated memory, finding the size is just a pointer subtraction then or b) record allocations and free memory in a data structure of some sort. For more details see this question, which has a good reference.
With glibc you can query the size of a given allocation fairly sensibly:
#include <iostream> #include <stdlib.h> #include <malloc.h> int main() { char *test = (char*)malloc(50); std::cout << malloc_usable_size(test) << "\n"; }
该信息可用于类似地释放/删除,并用于确定如何处理返回的内存块。
malloc_useable_size
的具体实现细节在 libc 源代码的 malloc/malloc.c 中给出:
(The following includes lightly edited explanations by Colin Plumb.)
Chunks of memory are maintained using a `boundary tag' method as described in e.g., Knuth or Standish. (See the paper by Paul Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such techniques.) Sizes of free chunks are stored both in the front of each chunk and at the end. This makes consolidating fragmented chunks into bigger chunks very fast. The size fields also hold bits representing whether chunks are free or in use.
An allocated chunk looks like this:
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk, if allocated | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of chunk, in bytes |M|P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | User data starts here... . . . . (malloc_usable_size() bytes) . . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
关于c++ - C++ 删除操作符如何找到多态对象的内存位置?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11745527/