C++ 删除指针后将其设为空是一种好做法吗?
首先,我要说,使用智能指针,你永远不用担心这个问题。 以下代码有什么问题C++ 删除指针后将其设为空是一种好做法吗?,c++,pointers,null,dynamic-allocation,C++,Pointers,Null,Dynamic Allocation,首先,我要说,使用智能指针,你永远不用担心这个问题。 以下代码有什么问题 Foo * p = new Foo; // (use p) delete p; p = NULL; 这是由另一个问题引发的。其中一条评论产生了几票赞成票: 在C++中设置指针为空的指针并不是通用的好方法。有时这是一件好事,有时它毫无意义,可以隐藏错误 在很多情况下,这是无济于事的。但根据我的经验,这不会有什么坏处。有人告诉我。我有一个更好的最佳实践:在可能的情况下,结束变量的作用域 { Foo* pFoo = n
Foo * p = new Foo;
// (use p)
delete p;
p = NULL;
这是由另一个问题引发的。其中一条评论产生了几票赞成票:
在C++中设置指针为空的指针并不是通用的好方法。有时这是一件好事,有时它毫无意义,可以隐藏错误
在很多情况下,这是无济于事的。但根据我的经验,这不会有什么坏处。有人告诉我。我有一个更好的最佳实践:在可能的情况下,结束变量的作用域
{
Foo* pFoo = new Foo;
// use pFoo
delete pFoo;
}
如果
删除后有更多代码,则选择“是”。在构造函数中或在方法或函数末尾删除指针时,否
这个寓言的目的是提醒程序员,在运行时,对象已经被删除了
一个更好的做法是使用智能指针(共享或范围)自动删除目标对象。
在删除了它指向的指针之后,将指针设置为NULL当然不会造成伤害,但它通常是一个带创可贴,而不是一个更基本的问题:为什么你首先使用指针?我可以看到两个典型的原因:
- 您只需要在堆上分配一些东西。在这种情况下,将其包装在RAII对象中会更安全、更干净。当不再需要对象时,结束RAII对象的作用域。这就是
std::vector
的工作原理,它解决了意外地将指针留在释放的内存中的问题。没有指针
- 或者您可能需要一些复杂的共享所有权语义。从
new
返回的指针可能与调用delete
的指针不同。多个对象可能同时使用了该对象。在这种情况下,最好使用共享指针或类似的东西
我的经验法则是,如果你在用户代码中留下指针,你就错了。指针首先不应该指向垃圾。为什么没有一个对象负责确保其有效性?为什么当指向对象的范围结束时,它的范围还没有结束?首先,在这方面存在很多问题,例如,与此密切相关的主题
在您的代码中,问题在于(使用p)。例如,如果您有这样的代码:
Foo * p2 = p;
然后将p设置为NULL完成的工作非常少,因为您仍然需要担心指针p2
这并不是说将指针设置为NULL总是毫无意义的。例如,如果p是一个成员变量,指向的资源的生命周期与包含p的类的生命周期不完全相同,那么将p设置为NULL可能是指示资源存在或不存在的一种有用方法。让我扩展一下您已经在问题中提出的内容
以下是您在问题中提出的要点:
在C++中设置指针为空的指针并不是通用的好方法。有时:
- 这是一件好事
- 有时它毫无意义,可以隐藏错误
然而,这并不是坏事!通过显式地将其置零,您不会引入更多的bug,您不会泄漏内存,您不会导致未定义的行为发生
因此,如果有疑问,请将其置空
话虽如此,如果您觉得必须显式地为某个指针设置空值,那么对我来说,这听起来好像您对方法的拆分不够,应该考虑一种称为“提取方法”的重构方法,将该方法拆分为单独的部分。始终需要担心。将指针设置为0(即“空”)在标准C++中,C的空定义有点不同,避免了双删除中的崩溃。
考虑以下几点:
Foo* foo = 0; // Sets the pointer to 0 (C++ NULL)
delete foo; // Won't do anything
鉴于:
Foo* foo = new Foo();
delete foo; // Deletes the object
delete foo; // Undefined behavior
换句话说,如果不将已删除指针设置为0,则如果执行双重删除,则会遇到麻烦。反对在delete之后将指针设置为0的一个理由是,这样做只会掩盖双重删除错误,并使它们无法处理
显然,最好不要有双重删除错误,但这取决于所有权语义和对象生命周期,在实践中很难实现。比起UB,我更喜欢屏蔽的双删除bug
最后,关于管理对象分配的一个旁注,我建议您查看std::unique_ptr
以获得严格/单一所有权,std::shared_ptr
以获得共享所有权,或者根据您的需要查看另一个智能指针实现。如果要在再次使用指针之前重新分配指针(取消引用它、将它传递给函数等),将指针设为NULL只是一个额外的操作。但是,如果在再次使用它之前不确定它是否将被重新分配,则将其设为NULL是一个好主意
正如许多人所说,仅仅使用智能指针当然要容易得多
编辑:正如Thomas Matthews在中所说,如果在析构函数中删除了指针,则无需为其指定NULL,因为该对象已被销毁,因此不再使用该指针。我将稍微更改您的问题:
你会使用未初始化的
指针?你知道的,一个你没有的
设置为NULL或为其分配内存
指向
有两种情况可以跳过将指针设置为NULL:
- 指针变量立即超出范围
- 您已经重载了指针的语义,并且不仅将其值用作内存指针,还将其用作键或原始值。但是,这种方法还存在其他问题
同时,认为将指针设置为NULL可能会对我隐藏错误,这听起来很奇怪
#define SAFEDELETE(ptr) { delete(ptr); ptr = NULL; }
static void TreeItem::DeleteSubtree(TreeItem *&rootObject)
{
if (rootObject == NULL)
return;
rootObject->UnlinkFromParent();
for (int i = 0; i < numChildren)
DeleteSubtree(rootObject->child[i]);
delete rootObject;
rootObject = NULL;
}
optional<Foo*> p (new Foo);
// (use p.get(), but must test p for truth first!...)
delete p.get();
p = optional<Foo*>();
shared_ptr<Foo> p(new Foo);
//No more need to call delete
delete myObj;
myobj = 0
lock(myObjMutex);
delete myObj;
myobj = 0
unlock(myObjMutex);
try{
delete myObj; //exception in destructor
myObj=0
}
catch
{
//myObj=0; <- possibly resource-leak
}
if (myObj)
// use myObj <--undefined behaviour