C ++放置新的,继承和析构函数

时间:2015-05-20 15:47:32

标签: c++ placement-new

研究员,

在类层次结构上使用placement-new时,基类必须进行解除分配。否则,将在取消分配的对象上调用基类析构函数。 我希望能够从派生类执行取消分配。所以我愿意接受想法和建议! (注意:我没有与placement-new结婚,但我想要自定义内存管理而不是new / delete)。

请在下面找到一段示例代码:

#include <cstdint>
#include <cstdio>
#include <new>

class CParent
{
public :
    CParent() {
        printf("CParent()\n");
    }

    virtual ~CParent() {
        printf("~CParent()\n");
    }
};

class CAllocator
{
private :
    void Free(uint8_t *buffer) {
        printf("CAllocator::Free(%p)\n", buffer);
        delete [] buffer;
    }

    class CChild : public CParent
    {
    public :
        CChild(CAllocator &allocator, uint8_t *buffer)
            : mAllocator(allocator), mBuffer(buffer)
        {
            printf("CChild()\n");
        }

        ~CChild() {
            printf("~CChild()\n");
            mAllocator.Free(mBuffer);
        }

    private :
        CAllocator &mAllocator;
        uint8_t *mBuffer;
    };

public :
    CParent *Alloc() {
        uint8_t *buffer = new uint8_t[sizeof(CChild)];
        printf("CAllocator::Alloc() = %p\n", buffer);
        return new (buffer) CChild(*this, buffer);
    }
};

int main()
{
    CAllocator allocator;
    CParent *object = allocator.Alloc();

    // NB: Can't do `delete object` here because of placement-new
    object->~CParent();
    return 0;
}

其中给出了以下输出:

CAllocator::Alloc() = 0x2001010
CParent()
CChild()
~CChild()
CAllocator::Free(0x2001010)
~CParent()

在释放内存后调用~CParent() ... 非常感谢你的帮助!

1 个答案:

答案 0 :(得分:1)

您正在混淆以下概念,以至于我认为您不清楚它们应该是什么:

  1. 基类/派生类析构函数。
  2. 展示位置new运营商。
  3. 内存分配和deallcation。
  4. 使用普通旧operator new分配对象时,会发生两件事:

    1. 为对象分配内存。
    2. 调用对象的构造函数(对于具有构造函数的类)。
    3. 当您对operator delete返回的指针调用operator new时,会发生两件事:

      1. 调用对象的析构函数。
      2. 内存已取消分配。
      3. 使用展示位置new运算符时,您必须:

        1. 在调用展示位置new运算符之前分配内存。
        2. new的调用中使用预先分配的内存。调用类的构造函数来初始化对象。
        3. 对于此类对象,您必须:

          1. 明确调用析构函数。
          2. 使用与分配内存的方式匹配的方法释放内存。如果使用operator new char[size];分配内存,请使用delete [] ptr;取消分配内存。如果您使用malloc(size)分配内存,请使用free(ptr)取消分配内存。
          3. 为了保持代码清洁,您应该分开:

            1. 分配和释放内存的责任。
            2. 调用构造函数和析构函数的责任。
            3. 在您发布的代码中,类CChild似乎是不洁净的。目前尚不清楚它是面向用户还是辅助类来帮助您管理内存。

              如果您认为它是面向类的用户,我会将代码重构为:

              #include <cstdint>
              #include <cstdio>
              #include <new>
              
              class CParent
              {
                 public :
                    CParent() {
                       printf("CParent()\n");
                    }
              
                    virtual ~CParent() {
                       printf("~CParent()\n");
                    }
              };
              
              class CChild : public CParent
              {
                 public :
                    CChild()
                    {
                       printf("CChild()\n");
                    }
              
                    ~CChild() {
                       printf("~CChild()\n");
                    }
              
                 private :
              };
              
              class CAllocator
              {
                 public :
                    void Free(uint8_t *buffer) {
                       printf("CAllocator::Free(%p)\n", buffer);
                       delete [] buffer;
                    }
              
              
                    uint8_t *Alloc(size_t size) {
                       uint8_t *buffer = new uint8_t[size];
                       printf("CAllocator::Alloc() = %p\n", buffer);
                       return buffer;
                    }
              };
              
              int main()
              {
                 CAllocator allocator;
                 uint8_t *buffer = allocator.Alloc(sizeof(CChild));
              
                 CParent* object = new (buffer) CChild;
              
                 object->~CParent();
              
                 allocator.Free(buffer);
              
                 return 0;
              }
              

              如果您想将CChild用作管理内存的辅助类,那么您首先必须确保CAllocator::Alloc()CAlloctor::Free()是对称的。由于Alloc()会返回指向CParent的指针,因此您需要更改Free()以接受指向CParent的指针并使用它做正确的事情。这就是我认为代码应该是这样的:

              #include <cstdint>
              #include <cstdio>
              #include <new>
              
              class CParent
              {
                 public :
                    CParent() {
                       printf("CParent()\n");
                    }
              
                    virtual ~CParent() {
                       printf("~CParent()\n");
                    }
              };
              
              class CAllocator
              {
                 private :
              
                    class CChild : public CParent
                    {
                       public :
                          CChild(uint8_t *buffer) : mBuffer(buffer)
                          {
                             printf("CChild()\n");
                          }
              
                          ~CChild() {
                             printf("~CChild()\n");
              
                             // The object has ownership of the buffer.
                             // It can deallocate it.
                             delete [] mBuffer;
                          }
              
                       private :
                          uint8_t *mBuffer;
                    };
              
                 public :
              
                    // Make Alloc and Free symmetric.
                    // If Alloc() returns a CParent*, make sure Free()
                    // accepts the same value and does the right thing
                    // with it.
              
                    CParent *Alloc() {
                       uint8_t *buffer = new uint8_t[sizeof(CChild)];
                       printf("CAllocator::Alloc() = %p\n", buffer);
              
                       // Transfer the ownership of buffer to CChild
                       return new (buffer) CChild(buffer);
                    }
              
                    void Free(CParent* object) {
                       printf("CAllocator::Free(%p)\n", object);
                       object->~CParent();
                    }
              
              
              };
              
              int main()
              {
                 CAllocator allocator;
              
                 CParent *object = allocator.Alloc();
                 allocator.Free(object);
              
                 return 0;
              }