2

假设一个指针对象被分配在一个点上,并且它被返回给不同的嵌套函数。有一次,我想在检查它是否有效或已被某人取消分配后取消分配该指针。

是否有任何保证这些都会起作用?

if(ptr != NULL)
   delete ptr;

或者

if(ptr)
   delete ptr;

此代码不起作用。它总是给出分段错误

#include <iostream>
class A
{
    public:
    int x;
     A(int a){ x=a;}
     ~A()
     { 
          if(this || this != NULL) 
              delete this;
     }
};
int main()
{ 
    A *a = new A(3);
    delete a;
    a=NULL;
}

编辑

每当我们谈论指针时,人们就会开始问,为什么不使用智能指针。仅仅因为有智能指针,每个人都无法使用它。

我们可能正在研究使用旧式指针的系统。美好的一天,我们无法将它们全部转换为智能指针。

4

5 回答 5

6

if(ptr != NULL) delete ptr;

或者

if(ptr) delete ptr;

这两者实际上是等价的,也与 相同delete ptr;,因为调用指针可以保证delete工作NULL(如,它什么都不做)。

如果ptr是一个悬空指针,它们不能保证工作。

意义:

int* x = new int;
int* ptr = x;
//ptr and x point to the same location
delete x;
//x is deleted, but ptr still points to the same location
x = NULL;
//even if x is set to NULL, ptr is not changed
if (ptr)  //this is true
   delete ptr;   //this invokes undefined behavior

在您的特定代码中,您会收到异常,因为您调用delete this了析构函数,而析构函数又再次调用了析构函数。因为thisis never NULL,你会得到一个堆栈溢出,因为析构函数将不受控制地递归。

于 2012-06-22T09:47:13.153 回答
4

不要调用delete this析构函数:

5.3.5、删除:如果delete-expression的操作数的值不是空指针值,delete-expression将为被删除的对象或数组元素调用析构函数(如果有)。

因此,您将在析构函数内进行无限递归。

然后:

if (p)
    delete p;

检查p不为空(if (x)在 C++ 中是指if x != 0)是多余的。delete已经检查过了。

这将是有效的:

class Foo {
public:
    Foo () : p(0) {}
    ~Foo() { delete p; }
private:
    int *p;

    // Handcrafting copy assignment for classes that store 
    // pointers is seriously non-trivial, so forbid copying:
    Foo (Foo const&) = delete;
    Foo& operator= (Foo const &) = delete;
};

不要假设任何内置类型,如intfloat或指向某物的指针,会自动初始化,因此,不要假设它们0在未显式初始化它们时会自动初始化(只有全局变量将被零初始化):

8.5 初始化器:如果没有为对象指定初始化器,则该对象是默认初始化的;如果不执行初始化,则具有自动或动态存储持续时间的对象具有不确定的值。[注意:具有静态或线程存储持续时间的对象是零初始化的

所以:总是初始化内置类型!


我的问题是我应该如何避免指针的双重删除并防止崩溃。

析构函数应该只输入和离开一次。不是零次,不是两次,一次。

如果您有多个可以到达指针的位置,但不确定何时允许删除,即如果您发现自己在记账,请使用更简单的算法、更简单的规则或智能指针,例如std::shared_ptror std::unique_ptr

class Foo {
public:
    Foo (std::shared_ptr<int> tehInt) : tehInt_(tehInt) {}
private:
    std::shared_ptr<int> tehInt_;
};

int main() {
    std::shared_ptr<int> tehInt;
    Foo foo (tehInt);
}
于 2012-06-22T09:50:51.737 回答
2

您不能假设指针在有人删除后会设置为 NULL。embarcadero C++ Builder XE 肯定就是这种情况。之后它可能会被设置为 NULL 但不要使用它不允许您的代码再次删除它的事实。

于 2012-06-22T09:48:40.303 回答
0

你问:“有一次,我想在检查它是否有效或已经被某人解除分配后解除分配。”

C/C++中没有可移植的方法来检查 >naked pointer< 是否有效。就是这样。故事到此结束。你不能这样做。再说一遍:仅当您使用指针或 C 样式指针时。还有其他类型的指针没有这个问题,所以为什么不使用它们呢!

现在问题变成了:你为什么坚持应该使用裸指针?不要使用裸指针,适当地使用std::shared_ptrstd::weak_ptr,你甚至不需要担心删除任何东西。当最后一个指针超出范围时,它将自动删除。下面是一个例子。

示例代码显示在堆上分配了两个对象实例:一个整数和一个 Holder。当 test() 返回时,std::auto_ptr<Holder>调用者不使用返回的值main()。因此指针被破坏,从而删除了 Holder 类的实例。当实例被破坏时,它会破坏指向整数实例的指针——指向该整数的两个指针中的第二个。然后myInt也被破坏,因此最后一个指向整数的指针被破坏,并且内存被释放。自动且无后顾之忧。

class Holder {
  std::auto_ptr<int> data;
public:
  Holder(const std::auto_ptr<int> & d) : data(d) {}
}

std::auto_ptr<Holder> test() {
  std::auto_ptr<int> myInt = new int;
  std::auto_ptr<Holder> myHolder = new Holder(myInt);
  return myHolder;
}

int main(int, char**) {
  test(); // notice we don't do any deallocations!
}

只是不要在 C++ 中使用裸指针,没有充分的理由。它只会让你在脚下开枪。多次。放弃;)

智能指针的粗略指南如下:

  • std::auto_ptr - 当作用域是对象的唯一所有者时使用,并且对象的生命周期在作用域死亡时结束。因此,如果auto_ptr是一个类成员,那么当类的实例被销毁时,指向的对象被删除必须是有意义的。将其用作函数中的自动变量也是如此。在所有其他情况下,请勿使用它。

  • std::shared_ptr - 它的使用意味着所有权,可能在多个指针之间共享。指向对象的生命周期在指向它的最后一个指针被销毁时结束。使管理对象的生命周期变得非常简单,但要注意循环引用。如果 Class1 拥有 Class2 的一个实例,而 Class2 的同一个实例拥有 Class1 的前一个实例,则指针本身永远不会删除这些类。

  • std::weak_ptr - 它的使用意味着非所有权。它不能直接使用,但必须shared_ptr在使用前转换回a。Aweak_ptr不会阻止对象被销毁,因此不会出现循环引用问题。否则它是安全的,因为如果它悬空,您将无法使用它。它将断言或向您提供一个空指针,从而导致立即的段错误。使用悬空指针更糟糕,因为它们通常看起来有效。

    这实际上是 的主要好处weak_ptr:使用裸 C 风格的指针,您永远不会知道是否有人删除了该对象。Aweak_ptr知道最后一次shared_ptr超出范围的时间,它将阻止您使用该对象。您甚至可以询问它是否仍然有效:如果对象被删除,该expired()方法将返回。true

于 2012-06-22T11:18:44.110 回答
-1

你永远不应该使用delete this. 有两个原因,析构函数正在删除内存并给你整理的机会(释放操作系统资源,删除对象创建的对象中的任何指针)。其次,对象可能在堆栈上。

于 2012-06-22T10:02:36.263 回答