我正在尝试重载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;()中调用构造函数。这里发生了什么? :)
谢谢!
答案 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)
您应该知道不能多次对单个地址执行删除。