简体   繁体   English

重载运算符删除不删除 object - 有效或未定义的行为?

[英]Overload operator delete to not delete object - valid or undefined behavior?

Preface: I'm optimizing an old codebase.前言:我正在优化一个旧的代码库。

I have a class named Token for which I added caching to for a subset of the tokens (but not all).我有一个名为Token的 class ,我为其添加了缓存以用于令牌的子集(但不是全部)。 Cached tokens may not be deleted as their pointers are stored in a permanent collection in memory for the whole lifetime of the program.缓存的令牌可能不会被删除,因为它们的指针在程序的整个生命周期内都存储在 memory 的永久集合中。

Unfortunately, the codebase has delete token all over the place.不幸的是,代码库到处都有delete token So what I did was add a bool cached member that is checked from inside Token::operator delete and destructor ~Token() , which returns from these respective functions immediately if cached is true.所以我所做的是添加一个bool cached成员,该成员从Token::operator delete和析构函数~Token()内部检查,如果cached为真,它会立即从这些各自的函数返回。

My question is, is this undefined behavior, or is it okay for me to do?我的问题是,这是未定义的行为,还是我可以这样做? Is it okay to execute the delete operator on something that doesn't get deleted?可以对未删除的内容执行delete运算符吗? Or will this bite me in the future?或者这会在未来咬我吗?

class Token
{
    bool cached;
    void* data;

public:
    ~Token()
    {
        if (this->cached) return;
        free(data);
    }

    void operator delete(void* p)
    {
        if (((Token*)p)->cached) return;
        ::operator delete(p);
    }

    // operator new, constructor etc.

}

This is not OK.这不行。 It is not UB to not free memory in operator delete , but it is UB to access anything inside the deleted object.operator delete中不释放 memory不是UB,但访问已删除 object 中的任何内容UB。 delete token will first call the destructor for token , and then it will call operator delete . delete token将首先调用token的析构函数,然后调用operator delete Even if you yourself do nothing inside the body of the destructor, once the body returns it will continue destroy all subobjects of the object, and once the whole destructor returns the whole object is considered to no longer exist.即使你自己在析构函数的主体内什么也不做,一旦主体返回它会继续销毁 object 的所有子对象,一旦整个析构函数返回整个 object 就被认为不再存在。 Even if your data members are scalars like bool s or void* s, which implementations don't usually touch when destroying, the language considers them to be made inaccessible by the destructor.即使您的数据成员是像bool s 或void* s 之类的标量,在销毁时通常不会触及这些实现,该语言也认为它们无法被析构函数访问。 In your example, operator delete cannot access cached , and later, if you still have a "cached" pointer to the destroyed object around, using that pointer is also UB.在您的示例中, operator delete无法访问cached ,稍后,如果您仍然有一个指向已破坏 object 的“缓存”指针,则使用该指针也是 UB。 This is why you receive a void* and not a Token* in operator delete , and also why operator delete is implicitly static (no this ): it signifies that your object is already gone.这就是您在operator delete中收到void*而不是Token*的原因,也是operator delete隐含为static (没有this )的原因:它表示您的 object 已经消失了。

If you're in C++20 land, you can use destroying operator delete instead:如果您在 C++20 领域,则可以使用破坏运算符 delete 代替:

struct Token {
   // ...
   bool cached;
   void operator delete(Token *thiz, std::destroying_delete_t) {
       if(thiz->cached) return;
       thiz->~Token();
       ::operator delete(thiz);
   }
   ~Token() { /* clean up without worrying about cached */ }
};

When such an overload of operator delete exists, delete token will call the overload without calling the destructor itself, so you can safely choose to simply not destroy the object.当存在这样的operator delete重载时, delete token将调用重载而不调用析构函数本身,因此您可以安全地选择不销毁 object。 Of course, now you're responsible for calling the destructor yourself.当然,现在你有责任自己调用析构函数。

If you can't do that, you're somewhat out of luck.如果你不能做到这一点,那你就有点不走运了。 You might try void Token::operator delete(void*) = delete;您可以尝试void Token::operator delete(void*) = delete; to find all uses of delete on Token s so you can replace them.Token上查找delete的所有用途,以便您可以替换它们。 Preferably you'd replace your (I presume) Token* s with some kind of smart pointer (whether std::shared_ptr or something you write yourself), so that you no longer need to have as many delete s at all.最好你用某种智能指针(无论是std::shared_ptr还是你自己写的东西)替换你的(我认为) Token* ,这样你就不再需要有尽可能多的delete了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM