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

时间:2009-12-21 11:24:13

标签: c++ operator-overloading reference-counting

我正在尝试重载operator delete,这样我就可以返回一个普通指针,指向那些不希望使用智能指针的人,并且能够控制删除对象的时间。

我定义了一个由多个灵魂构造的类Cat,有一个重载的操作符删除,什么都不做,以及析构函数减少了灵魂的数量(并且还做了一些吹牛)。当灵魂达到0时,析构函数调用global :: delete,然后cat死掉。

这听起来很简单,但不能按预期工作。这是代码:

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编译,可能是编译器错误吗?

BTW,当我使用运算符&lt;&lt;(&)时,如cout&lt;&lt; * p而不是repotr(),它也很奇怪:它进入了一个无限循环,从运算符&lt;&lt;()中调用构造函数。这里发生了什么? :)

谢谢!

5 个答案:

答案 0 :(得分:19)

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

答案 1 :(得分:7)

运算符delete调用对象析构函数,然后在无人区域。正如其他人指出的那样,你想做的事情是不可能的。

当在堆上和堆栈上构造对象时,您正在做的事情也有点不协调。

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

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

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

答案 2 :(得分:2)

一旦调用了对象的析构函数,就不会对该对象执行任何操作。你不想做什么。

答案 3 :(得分:1)

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

第一个电话正在释放内存,第二个电话响了。

答案 4 :(得分:1)

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