gpt4 book ai didi

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

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:18:14 24 4
gpt4 key购买 nike

同学们,

在类层次结构上使用 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. 基类/派生类析构函数。
  2. 放置运算符。
  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() = %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;
}

关于C++放置new、继承和析构函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30354497/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com