假设我们有这样的情况。假设不是“p =& global;”而是我们调用了一些函数(由某人编写,使我们的指针无效)。怎么处理这个问题?如何保护代码免受崩溃?我知道并使用boost智能指针。但是如果我们遇到这种情况该怎么办。
struct Test
{
int a;
int b;
int c;
};
Test global;
int main()
{
Test *p = new Test;
p->a = 1;
p->b = 2;
p->c = 3;
p = &global;
delete p;
return 0;
}
答案 0 :(得分:17)
您可以通过修复错误并重新编译程序来处理它。其他任何事都没有意义。
答案 1 :(得分:9)
你不能也不应该尝试处理这种情况,除非不让它首先出现 C ++中有一些基本规则必须遵守。
答案 2 :(得分:5)
无。如果你这样做,那么你得到你得到的。不要这样做。
重新分配p
后,泄漏了Test
最初指向的p
对象。你现在已经在这个应用程序的运行时间内丢失了内存。然后,当您删除非堆对象时,您将遇到未定义的行为,并且任何事情都可能发生(通常运行时库将崩溃尝试删除非堆内存 - 但您无法保证)。一旦你试图删除非堆内存,你就无法做到可靠。
您已经提到了智能指针,这是解决方案的一部分。另一部分只是小心。
答案 3 :(得分:4)
不幸的是,你无能为力。 C ++编译器无法从您的代码中判断您是否可能在将来删除指针,因此您必须确保在代码中正确管理它们。这意味着如果将非堆分配项的地址放入指针,则删除该指针是你的责任。
简而言之,C ++无法保护您免受可能编写的每一个错误。
答案 4 :(得分:2)
您可以使用以下代码查明指针是指向堆栈区域还是堆区域:
bool IsMemoryOnStack( void* p )
{
void* dwStackTop = 0;
void* dwStackLowCurrent = 0;
__asm {
mov EAX, FS:[4]
mov dwStackTop, eax
mov EAX, FS:[8]
mov dwStackLowCurrent, eax
}
return ( p<= dwStackTop && p>= dwStackLowCurrent );
}
答案 5 :(得分:1)
您需要交换分配和删除语句:
delete p;
p = &global;
但我建议永远不要使用相同的变量来指向需要显式空闲的数据和不需要显式空闲的数据。为每个变量选择一个或另一个,因此您可以在重新分配之前始终删除内存,或者永远不删除它。如果你试着跟踪你的指针是如何设置的,那么你最终会花时间抱怨C ++如何不提供内存管理并迫使你编写不可维护的代码。
答案 6 :(得分:1)
避免这种情况的主要方法是避免在任何情况下使用new
或delete
。
在new
内使用main
特别可疑 - 在C ++中使用new
的原因是当你需要创建一个需要的对象时比它的创建范围更长(例如,当你到达该函数的末尾时,它必须不被销毁)。在main
的情况下,仅的理由是,如果您在main
中分配了一些不在主要内容中删除的内容,但是一些全局对象的析构函数在你从main
返回后运行时很少使用(这很少做,很少有好主意)。
new
的大部分用法应该在对象的ctor中,并且delete
的大多数用法应该在对象的dtor中。如果你有类似集合的东西,那么在一些其他成员函数中使用new
和/或delete
也是有意义的,这些函数可以处理重新调整集合大小等事情。 / p>
除此之外,还有一些实体对象通常不会被分配或复制。例如,考虑一个呼叫路由系统,您可以在有人拨打电话时创建“呼叫”对象,并在挂断时销毁该对象。在几乎所有这种情况下,您都有一个全局集合(或多个集合)来保存指向这些对象的指针,因此只要您创建对象,其地址就会进入(正确的)全局集合。有趣的是:几乎所有我看过这个模式有意义的代码不都有任何破坏对象的外部代码 - 相反,对象本身负责将自己从全局连接中删除,当附加的真实世界连接(或其他任何连接)结束时,使用(很多争论的)delete this;
来摧毁自己。
答案 7 :(得分:0)
可以重载delete
。从理论上讲,除非地址有效,否则您可能会让重载的delete
拒绝做任何事情。但是你怎么知道它是否有效?你能说的最好的是“这不是用new
分配的”,但你可能不得不重载new
来做到这一点。
对于记录,在这种情况下标准new
和delete
崩溃,因为delete
确定地址不是来自new
并且假设最差。假设最坏的情况可能是在这种情况下最好的事情;至少它胜过假设最好。
所以我会建议不要在代码中防止这种情况,并且根本不这样做。