Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/136.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 删除指针后将其设为空是一种好做法吗?_C++_Pointers_Null_Dynamic Allocation - Fatal编程技术网

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