c++ - C++ 删除操作符如何找到多态对象的内存位置?

标签 c++ memory-management g++

我想知道当给定一个与对象的真实内存位置不同的基类指针时,删除运算符如何计算出需要释放的内存位置。

我想在我自己的自定义分配器/释放器中复制此行为。

考虑以下层次结构:

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 .我怀疑通常也无法找到对象的真实内存位置。

注意事项:

最佳答案

这显然是特定于实现的。在实践中,实现事物的明智方法相对较少。从概念上讲,这里有一些问题:

  1. 您需要能够获得指向最派生对象的指针,即(从概念上)包含所有其他类型的对象。

    在标准 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
    
  2. 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/

相关文章:

c++ - QML 中的 ListView - 空行

c++ - 重新定义派生类继承的内部函数对基类产生影响

c++ - Valgrind 未检测到危险的释放内存

c++ - 可以为 3rd 方库取代/取代默认 C++ std::allocator 吗?

c++ - 仅静态链接 boost 库 G++

C++ 无法将对象设置为方法的返回对象

c++ - 我可以将参数类型分支到 C++17 中的参数包吗?

ruby-on-rails - PostgreSQL安装报错--无法分配内存

c++ - `std::mem_fn`如何在gcc 7.1.0中检测到noexceptness

c++ - g++ 和严格溢出的问题