C++放置new、继承和析构函数

C++ placement new, inheritance and destructor

本文关键字:析构函数 继承 放置 new C++      更新时间:2023-10-16

研究员

在类层次结构上使用placement new时,基类必须执行取消分配。否则,基类析构函数将在取消分配的对象上调用。我希望能够从派生类中执行取消分配。所以我对想法和建议持开放态度!(注意:我不适合放置新的,但我想有自定义的内存管理,而不是新的/删除(。

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

#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() = %pn", 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. 基类/派生类析构函数
  2. 放置new运算符
  3. 内存分配和释放

当您使用普通的旧operator new来分配对象时,会发生两件事:

  1. 已为对象分配内存
  2. 调用对象的构造函数(对于具有构造函数的类(

当您对operator new返回的指针调用operator delete时,会发生两件事:

  1. 调用对象的析构函数
  2. 内存已解除分配

当您使用放置new操作符时,您必须:

  1. 在调用放置new操作符之前分配内存
  2. 在对new的调用中使用预先分配的内存。调用类的构造函数来初始化对象

对于此类对象,您必须:

  1. 显式调用析构函数
  2. 使用与内存分配方式匹配的方法取消分配内存。如果使用operator new char[size];分配内存,请使用delete [] ptr;取消分配内存。如果使用malloc(size)分配内存,请使用free(ptr)取消分配内存

为了保持你的代码干净,你应该分开:

  1. 内存分配和释放的责任
  2. 调用构造函数和析构函数的责任

在您发布的代码中,类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() = %pn", 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() = %pn", 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;
}