我有一个下面提到的C ++代码:
#include<iostream>
#include<stdexcept>
const long MAX = 10240000;
class Widget{
public:
Widget(){
ok = new int [1024];
prob = new int [100*MAX];
}
~Widget(){
std::cout<<"\nDtoR of Widget is called\n";
delete ok; ok = NULL;
delete prob; prob = NULL;
}
//keeping below public: Intentionally
int* ok;
int* prob;
};
void func(){
Widget* pw = NULL; // <<<<--------------- Issue in Here
try{
pw = new Widget;
}
catch(...){
delete pw;
std::cout<<"\nIn catch BLOCK\n";
if(pw->ok == NULL){
std::cout<<"\n OK is NULL\n";
}
throw;
}
delete pw;
}
int main(){
try{
func();
}
catch(std::bad_alloc&){
std::cout<<"\nError allocating memory."<<std::endl;
}
std::cout<<std::endl;
system("pause");
return 0;
}
现在在函数func()中,我看到两种不同的行为,具体取决于我是否设置指针&#39; pw&#39;为NULL,如果我设置&#39; pw&#39;指向NULL的指针(如上面的代码)。我的印象是它很好'&#39;练习首先将指针设置为NULL然后初始化它。但是当我将它初始化为NULL时,输出只显示&#34;在catch BLOCK&#34;后来的应用程序崩溃。但是如果我没有将pw指针设置为NULL,那么我会看到pw的析构函数被调用,并且显示以下输出没有任何应用程序崩溃。
Widget的DtoR称为
抓住BLOCK
确定是NULL
分配内存时出错。
按任意键继续。 。
我的问题是,为什么在我们没有初始化&#39; pw&#39;指向NULL的指针,在其他情况下,我们将其设置为NULL。为什么在一个案例中调用析构函数,为什么不在另一个案例中调用析构函数。
注意:此代码的目的是抛出bad_alloc exeption。
答案 0 :(得分:6)
如果您未将pw
设置为NULL
,那么您将保持未初始化状态。然后,当“try”块中的new
运算符抛出异常时,它永远不会返回,并且您进入catch
块。由于new
从未返回,pw
仍未初始化,您将随机地址传递给delete
。这会给你一个未定义的行为。
答案 1 :(得分:4)
在你的catch区块中,你有:
if(pw->ok == NULL)
此时,pw
为NULL
(如果您没有初始化,则为垃圾)。 pw-ok
尝试取消引用它,给出未定义的行为(在这种情况下崩溃)。
如果你没有初始化它,那么delete pw
将在打印“catch”消息之前崩溃;最有可能的是,它会在崩溃之前打印出“Dtor”消息,但由于你坚定地处于未定义的行为领域,因此无法保证。
如果你做了初始化,那么delete pw
是不必要但无害的;删除空指针定义为什么都不做。所以在这种情况下,在取消引用它之前,你不会崩溃。
无论如何,你有一个无法修复的内存泄漏 - 第一个分配ok = new int[1024]
将成功,但你已经失去了唯一的指针。这就是为什么你应该总是使用智能指针,容器和其他RAII技术管理动态内存。
答案 2 :(得分:1)
你打算使用bad_alloc exeption.but你有更多未处理的异常! 首先删除pw然后使用它的指针是不行的!
delete pw;
if(pw->ok == NULL)
答案 3 :(得分:0)
由于行
,将pw设置为NULL时,您看到应用程序崩溃if (pw->ok == NULL)
您正在取消引用导致崩溃的NULL。在另一种情况下,您正在删除垃圾,这将给您未定义的行为。
此外,在调用delete之后不应使用指针。这可能会导致各种奇怪的行为。
详细说明正在发生的事情。你的Widget构造函数抛出了分配异常。在这种情况下,ok
的分配很可能已完成,但prob
的分配失败。您的构造函数永远不会完成,泄漏分配给ok
变量的内存。如果要确保清理内存,则应在构造函数中添加try catch。
Widget() : ok(NULL), prob(NULL)
{
try
{
ok = new int [1024];
prob = new int [100*MAX];
}
catch(...)
{
std::cout << "\nCtor of Widget threw exception\n";
delete [] ok;
delete [] prob;
throw;
}
}
答案 4 :(得分:0)
pw->ok
后,为什么要拨打pw
?它已经消失了。
Widget():ok(NULL), prob(NULL) {
...
}
因为如果Widget()失败,你不知道哪个成员变量被初始化,哪个不能导致你的析构函数出现问题。
int[]
进行了分配,因此您需要在析构函数中delete[]
而不仅仅是delete
。答案 5 :(得分:-1)
最好将pw
初始化为NULL
,但删除它时,首先应检查pw是否为空。类似的东西:
if (pw) delete pw;
此外,如果pw
为NULL
,则无法引用其成员。