C++放置new、继承和析构函数
C++ placement new, inheritance and destructor
同学们,
在 class 层次结构上使用 placement-new 时,基础 class 必须执行取消分配。否则,base class 析构函数将在释放的对象上调用。
我希望能够从派生 class 执行取消分配。所以我愿意接受想法和建议! (注意:我没有嫁给 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()
在内存释放后被调用...
非常感谢您的帮助!
你混合了以下概念,让我觉得你不清楚它们应该是什么:
- 基础 classs/derived class 析构函数。
- 放置
new
运算符。
- 内存分配和释放。
当您使用普通的旧 operator new
分配对象时,会发生两件事:
- 已为对象分配内存。
- 调用对象的构造函数(对于具有构造函数的 classes)。
当您对 operator new
返回的指针调用 operator delete
时,会发生两件事:
- 对象的析构函数被调用。
- 内存已释放。
当您使用展示位置 new
运算符时,您必须:
- 在调用放置
new
运算符之前分配内存。
- 在对
new
的调用中使用预分配的内存。调用 class 的构造函数来初始化对象。
对于此类对象,您必须:
- 显式调用析构函数。
- 使用与内存分配方式相匹配的方法释放内存。如果您使用
operator new char[size];
分配内存,请使用 delete [] ptr;
释放内存。如果您使用 malloc(size)
分配内存,请使用 free(ptr)
释放内存。
为了保持你的代码干净,你应该分开:
- 分配和释放内存的责任。
- 负责调用构造函数和析构函数。
在您发布的代码中,class CChild
似乎不干净。目前尚不清楚它是面向用户 class 还是帮手 class 来帮助您管理内存。
如果你的意思是面向 class 的用户,我会将代码重构为:
#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
用作帮助程序 class 来管理内存,那么您要做的第一件事就是确保 CAllocator::Alloc()
和 CAlloctor::Free()
是对称的。由于 Alloc()
returns 指向 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;
}
同学们,
在 class 层次结构上使用 placement-new 时,基础 class 必须执行取消分配。否则,base class 析构函数将在释放的对象上调用。 我希望能够从派生 class 执行取消分配。所以我愿意接受想法和建议! (注意:我没有嫁给 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()
在内存释放后被调用...
非常感谢您的帮助!
你混合了以下概念,让我觉得你不清楚它们应该是什么:
- 基础 classs/derived class 析构函数。
- 放置
new
运算符。 - 内存分配和释放。
当您使用普通的旧 operator new
分配对象时,会发生两件事:
- 已为对象分配内存。
- 调用对象的构造函数(对于具有构造函数的 classes)。
当您对 operator new
返回的指针调用 operator delete
时,会发生两件事:
- 对象的析构函数被调用。
- 内存已释放。
当您使用展示位置 new
运算符时,您必须:
- 在调用放置
new
运算符之前分配内存。 - 在对
new
的调用中使用预分配的内存。调用 class 的构造函数来初始化对象。
对于此类对象,您必须:
- 显式调用析构函数。
- 使用与内存分配方式相匹配的方法释放内存。如果您使用
operator new char[size];
分配内存,请使用delete [] ptr;
释放内存。如果您使用malloc(size)
分配内存,请使用free(ptr)
释放内存。
为了保持你的代码干净,你应该分开:
- 分配和释放内存的责任。
- 负责调用构造函数和析构函数。
在您发布的代码中,class CChild
似乎不干净。目前尚不清楚它是面向用户 class 还是帮手 class 来帮助您管理内存。
如果你的意思是面向 class 的用户,我会将代码重构为:
#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
用作帮助程序 class 来管理内存,那么您要做的第一件事就是确保 CAllocator::Alloc()
和 CAlloctor::Free()
是对称的。由于 Alloc()
returns 指向 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;
}