重载运算符delete,或者如何杀死一只猫?

发布于 2024-08-15 17:53:20 字数 2166 浏览 8 评论 0原文

我正在尝试重载运算符删除,以便我可以向那些不希望使用智能指针的人返回一个普通指针,但又能够控制何时删除对象。

我定义了一个由多个灵魂构造的类 Cat,它有一个不执行任何操作的重载运算符删除,以及减少灵魂数量的析构函数(并且还进行了一些吹牛)。当灵魂达到 0 时,析构函数调用全局 ::delete,猫就死了。

这听起来很简单,但并没有按预期工作。这是代码:

class Cat {
public:
    Cat(string n): name(n), souls(9)
    { cout << "Myaou... " << name << " is born\n"; }

    ~Cat();
    void operator delete(void *p) { cout << "!!! operator delete called\n"; }
    void report()
    { cout << name << "'s here, " << souls << " souls to spend\n"; }

    friend ostream& operator<< (const ostream& o, const Cat& cat);
private:
    void kill();
    const string name;
    int souls;
};

Cat::~Cat()
{
    cout << "!!! dtor called\n";
    kill();
}

void Cat::kill()
{
    if (--souls)
        cout << name << " is still alive! I have " << souls << " souls left.\n";
    else {
        cout << name << " is dying... good bye world!\n";
        ::delete((void*)this);
    }
}

ostream& operator<< (const ostream& o, const Cat& cat)
{
    return o << cat.name << "'s here, " << cat.souls << " souls to spend\n";
}

这是主要部分:

int main()
{
    Cat *p = new Cat("Mitzi");

    for (;;)
    {
        char c[100];
//      cout << *p;
        p->report();
        cout << "come on, hit me!";
        cin >> c;
        delete p;
    }
}

我预计循环会运行 9 次,然后会发生一些不愉快的事情(崩溃)。但是,这是输出:

Myaou... Mitzi is born
Mitzi's here, 9 souls to spend
come on, hit me!c
!!! dtor called
Mitzi is still alive! I have 8 souls left.
!!! operator delete called
's here, 8 souls to spend
come on, hit me!c
!!! dtor called
 is still alive! I have 7 souls left.
*** glibc detected *** /home/davidk/workspace/string_test/Debug/string_test: double free or corruption (fasttop): 0x080cd008 ***

似乎在第一次删除后 name 成员被销毁,下一次删除导致崩溃。有什么解释吗?我在 Linux 上使用 gcc 进行编译,可能是编译器错误吗?

顺便说一句,当我在 cout << 中使用运算符 <<() 时*p 而不是 repotr(),这也很奇怪:它进入了从operator<<() 内部调用构造函数的无限循环。这是怎么回事? :)

谢谢!

I am experimenting with overloading operator delete, so that I can return a plain pointer to those who don't wish to work with smart pointers, and yet be able to control when the object is deleted.

I define a class Cat that is constructed with several souls, has an overloaded operator delete that does nothing, and destructor that decrements the number of souls (and also does some bragging). When souls reaches 0 the destructor calls the global ::delete, and the cat dies.

This sounds quite simple, but does not work as expected. Here's the code:

class Cat {
public:
    Cat(string n): name(n), souls(9)
    { cout << "Myaou... " << name << " is born\n"; }

    ~Cat();
    void operator delete(void *p) { cout << "!!! operator delete called\n"; }
    void report()
    { cout << name << "'s here, " << souls << " souls to spend\n"; }

    friend ostream& operator<< (const ostream& o, const Cat& cat);
private:
    void kill();
    const string name;
    int souls;
};

Cat::~Cat()
{
    cout << "!!! dtor called\n";
    kill();
}

void Cat::kill()
{
    if (--souls)
        cout << name << " is still alive! I have " << souls << " souls left.\n";
    else {
        cout << name << " is dying... good bye world!\n";
        ::delete((void*)this);
    }
}

ostream& operator<< (const ostream& o, const Cat& cat)
{
    return o << cat.name << "'s here, " << cat.souls << " souls to spend\n";
}

here's the main:

int main()
{
    Cat *p = new Cat("Mitzi");

    for (;;)
    {
        char c[100];
//      cout << *p;
        p->report();
        cout << "come on, hit me!";
        cin >> c;
        delete p;
    }
}

I'd expect that the loop would run for 9 times, and then something unpleasant (crash) would happen. However, this is the output:

Myaou... Mitzi is born
Mitzi's here, 9 souls to spend
come on, hit me!c
!!! dtor called
Mitzi is still alive! I have 8 souls left.
!!! operator delete called
's here, 8 souls to spend
come on, hit me!c
!!! dtor called
 is still alive! I have 7 souls left.
*** glibc detected *** /home/davidk/workspace/string_test/Debug/string_test: double free or corruption (fasttop): 0x080cd008 ***

Seems that after the first delete the name member is destroyed, and the next delete causes a crash. Any explanations? I am compiling with gcc on Linux, can be a compiler bug?

BTW, when I used the operator<<() as in cout << *p instead of repotr(), it was also weird: it entered an infinite loop of calling the constructor from within the operator<<(). What's going on here? :)

thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

南笙 2024-08-22 17:53:20

您滥用了所有可能的 C++ 概念,这会导致错误。当第一次调用 delete p; 时,C++ 调用 Cat::~Cat() 析构函数,隐式销毁 std::string实体内部。当第二次调用 delete p; 时,重新运行 Cat::~Cat() 并重新运行 std::string 的析构函数这会导致未定义的行为使程序崩溃,因为我认为 std::string::~string() 不会使指向缓冲区的指针无效,因此当 std::string::~ string() 尝试使用相同的地址第二次释放缓冲区,这导致了著名的双重释放。

You've misused just every C++ concept possible and this causes errors. When delete p; is called for the first time C++ calls the Cat::~Cat() destructor which implicitly destroys the std::string inside the entity. When delete p; is called the second time Cat::~Cat() is rerun and it reruns the destructor of the std::string and this causes undefined behavour crashing the program because I suppose std::string::~string() doesn't nullify the pointer to the buffer and so when std::string::~string() tries to release the buffer for the second time with the same address this leads to famous double-free.

听你说爱我 2024-08-22 17:53:20

运算符delete调用对象析构函数,之后你就进入了无人区。正如其他人指出的那样,您尝试做的事情是不可能的。

当在堆和堆栈上构造对象时,您所做的事情也有点不一致,行为不一致。

如果您的想法是重写运算符delete,那么如果使用 new 构造对象,该对象将根据某些内部逻辑保持活动状态(生命数已达到 9)。

当在堆栈上构造时(Cat cat("Chesire cat");),对象在超出范围时将总是被破坏。为了实现您想要做的事情,您还需要将析构函数的行为更改为“停止析构”。这是不可能的,也是有充分理由的。

因此,如果您想要引用计数,只需实现您自己的机制即可。毕竟,如果您至少没有做过一次自己的引用计数内存管理,您就不能称自己为 C++ 程序员:))

operator delete calls the object destructor and after that you are in no man's land. As others pointed out, what you are trying to do is not possible.

What you are doing is also a bit dodgy in the way of inconsistent behaviour when the object is constructed on the heap and on the stack.

With your idea to override operator delete the object will stay alive depending on some internal logic (the number of lives has reached 9) if constructed using new.

When constructed on the stack (Cat cat("Chesire cat");) the object will always get destructed when it goes out of scope. In order to achive what you are trying to do you will also need to change the bahaviour of the destructor to "stop destructing". This is not possible, also for very good reasons.

So, if you want ref counting, just implement your own mechanism. After all, you can't call yourself a C++ programmer if you haven't done your own ref count memory management at least once :))

悲念泪 2024-08-22 17:53:20

一旦调用了对象的析构函数,您对该对象所做的任何操作都是未定义的。你想做的事是不可能的。

Once the destructor for an object has been called, anything you do with the object is undefined. What you are trying to do is not possible.

醉态萌生 2024-08-22 17:53:20

析构函数不负责释放内存,并且您没有阻止这种情况的发生。

第一个调用是释放内存,第二个调用会爆炸。

The destructor isn't responsible for freeing the memory and u've not prevented this from happening.

The first call is freeing the memory, the second call goes bang.

顾铮苏瑾 2024-08-22 17:53:20

您应该知道,您不能对单个地址执行多次删除。

You should know that you can't perform delete on a single address more than once.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文