4

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

我定义了一个 Cat 类,它由多个灵魂构成,有一个不执行任何操作的重载运算符 delete,以及减少灵魂数量的析构函数(并且还做一些吹牛)。当 souls 达到 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 ***

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

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

谢谢!

4

5 回答 5

20

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

于 2009-12-21T11:31:05.000 回答
7

操作员delete调用对象析构函数,然后你就在无人区。正如其他人指出的那样,您尝试做的事情是不可能的。

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

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

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

所以,如果你想要引用计数,只需实现你自己的机制。毕竟,如果您至少一次没有完成自己的引用计数内存管理,那么您就不能称自己为 C++ 程序员 :))

于 2009-12-21T11:46:29.967 回答
2

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

于 2009-12-21T11:30:28.277 回答
1

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

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

于 2009-12-21T11:29:53.520 回答
1

你应该知道你不能对一个地址多次执行删除。

于 2009-12-21T11:32:13.657 回答