重载运算符delete,或者如何杀死一只猫?
我正在尝试重载运算符删除,以便我可以向那些不希望使用智能指针的人返回一个普通指针,但又能够控制何时删除对象。
我定义了一个由多个灵魂构造的类 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您滥用了所有可能的 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 theCat::~Cat()
destructor which implicitly destroys thestd::string
inside the entity. Whendelete p;
is called the second timeCat::~Cat()
is rerun and it reruns the destructor of thestd::string
and this causes undefined behavour crashing the program because I supposestd::string::~string()
doesn't nullify the pointer to the buffer and so whenstd::string::~string()
tries to release the buffer for the second time with the same address this leads to famous double-free.运算符
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 :))
一旦调用了对象的析构函数,您对该对象所做的任何操作都是未定义的。你想做的事是不可能的。
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.
析构函数不负责释放内存,并且您没有阻止这种情况的发生。
第一个调用是释放内存,第二个调用会爆炸。
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.
您应该知道,您不能对单个地址执行多次删除。
You should know that you can't perform delete on a single address more than once.