C++ 删除应该什么都不做

c++ delete should do nothing

本文关键字:什么 删除 C++      更新时间:2023-10-16

问题:

有没有办法让删除的行为像一个假人,即在调用时什么都不做?

为什么我需要答案:

我正在使用一个自定义内存池,当静态方法调用 Pool::freeAllObjects() 时,它会从内存中释放所有对象。

所有类都有一个重载运算符 new,如下所示:

void * operator new (size_t size)
{
void * obj = Pool::memory()->allocate(size);
Pool::memory()->save_destructor((ClassName*)obj);
return obj;
}

Pool::memory()->save_destructor()只是保存一个指向运行泛型类型析构函数的函数的指针。

如果没有人在用池创建的对象上调用delete,那么一切都会正确运行,但在我们想要使用它的代码中,有许多对象的实例delete调用,所以为了向后兼容,我试图像这样重载delete

void operator delete(void*) {/*do nothing*/}

在具有重载的类中 new 使用 Pool::memory(),但看起来这并没有解决问题。我使用一个简单的 std::cout 来查看调用的 cons/析构函数和重载的删除,例如:

void operator delete(void*) {std::cout << "deleting ClassName" << std::endl;}

所以在这样的代码中:

ClassName * instance = new ClassName();
instance->runMethod();
delete instance; //this is legacy code calling delete before the pool was developed
/*
 other code goes here
*/
Pool::freeAllObjects(); // oops instance was already deleted

我得到以下输出:

constructing ClassName <-- 构造函数调用

destructing ClassName <-- 由于调用删除而调用析构函数

deleting ClassName <-- 删除显示其消息

destructing ClassName <-- 析构函数

之所以调用,是因为 Pool::freeAllObjects 在对象上运行析构函数

编辑:抱歉,我没有提到要求是不使用智能指针,并且不允许将运算符删除设为私有(顺便说一下,我认为这是最好的选择(。

你不能阻止delete调用析构函数;那就是它的作用。 但既然你抓住了operator delete稍后调用的函数,您可以使用某种隐藏旗。 像这样:

union MemoryHeader
{
    bool hasBeenDeleted;
    double forAlignment;
};
void* operator new ( size_t size )
{
    MemoryHeader* hdr = static_cast<MemoryHeader*>(
                Pool::memory()->allocate( size + sizeof( MemoryHeader ) ) );
    hdr->hasBeenDeleted = false;
    void* obj = hdr + 1;
    Pool::memory()->save_destructor( (ClassName*)hdr );
    return obj;
}
void operator delete( void* obj )
{
    MemoryHeader* hdr = static_cast<MemoryHeader*>( obj ) - 1;
    hdr->hasBeenDeleted = true;
}

然后,当您运行删除程序时,您可以检查标志。

或者甚至更好,在你的情况下;在你的operator delete函数,只需取消注册对象的析构函数:添加Pool::memory()返回的内容的clear_destructor函数,并调用它。

operator delete是一个释放函数

删除表达式首先调用析构函数,然后调用相关的释放函数。

"Pool::memory((->save_destructor(( 只是保存一个指向运行泛型类型析构函数的函数的指针。相反,如果要共享释放责任,则使用 std::shared_ptr 在更高级别处理它(您可以将类限制为只能通过生成 std::shared_ptr 的函数实例化(。这就是std::shared_ptr的目的。

由于分配器负责销毁对象,高低级关注点被混为一谈,这会导致复杂性和可能的错误。

将这些问题分开,例如通过使用std::shared_ptr,你会得到一个更简单、更可靠的设计,这也将更符合C++语言中的这种分离。


Andrei Alexandrescu的经典著作"Modern C++ Design"详细讨论了如何实现自定义分配方案。很有可能您可以在洛基图书馆中找到该分配器。我相信它是开源的。


如果你想要一个类似Java的自动垃圾收集,那么你可以使用Boehm收集器。我知道有些人已经在大中型项目中成功地使用了它。但是,我没有直接经验,但请检查一下,如果这就是您的目标。

如果你想

避免析构函数被自动调用,你必须实现placement new运算符,在这种情况下,必须显式调用析构函数(请参阅C++常见问题解答(。

如果要通过delete语句禁用释放内存,则重载它是一个好主意,无论是全局(如果您知道自己在做什么(还是每个类(可能使用继承(。 new/delete运算符是继承的(。

这完全取决于您要实现的目标以及您必须保持的与现有系统的兼容性。

无论哪种方式 - 都是可行的。另一个问题是 - 这是应该这样做的方式。

您可以考虑的另一种方法是禁用相关类的delete

只需使析构函数protectedprivate,然后将池分配器添加为friend

强制执行了现有设计,而不是通过增加复杂性来支持不正确的使用(另一个答案(,或者将设计更改为更简单和干净的东西(我的另一个答案(。