假设正在一个点上分配指针对象,并且它正被返回到不同的嵌套函数。有一次,我想在检查它是否有效或已被某人解除分配后取消分配该指针。
是否有任何保证可以使用?
if(ptr != NULL)
delete ptr;
OR
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;
}
修改
每当我们谈论指针时,人们就开始问,为什么不使用Smart Pointers。 仅仅因为智能指针存在,每个人都无法使用它。
我们可能正在使用旧式指针的系统。我们无法将所有这些转换为智能指针,一个晴朗的日子。
答案 0 :(得分:6)
if(ptr != NULL) delete ptr;
OR
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
,而析构函数又会再次调用析构函数。由于this
永远不会NULL
,因此析构函数将无法控制递归,因此您将获得STACK OVERFLOW。
答案 1 :(得分:4)
不要在析构函数中调用delete this
:
5.3.5,删除:如果delete-expression的操作数值不是空指针值,则delete-expression将 为对象或要删除的数组的元素调用析构函数(如果有)。
因此,你将在析构函数中有无限递归。
然后:
if (p)
delete p;
检查p
是否为空(C ++中的if (x)
表示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;
};
不要假设任何内置类型(如int
,float
或指向某事物的指针)要自动初始化,因此,如果没有明确初始化它们,请不要认为它们是0
(只有全局变量将被零初始化):
8.5初始值设定项:如果没有为对象指定初始化程序,则默认初始化该对象;如果没有执行初始化,则 具有自动或动态存储持续时间的对象具有不确定的值。 [注意:具有静态或线程存储持续时间的对象是零初始化
所以:始终初始化内置类型!
我的问题是我应该如何避免双重删除指针并防止崩溃。
应该输入析构函数并将其保留一次。不是零次,不是两次,一次。
如果您有多个可以到达指针的地方,但不确定何时允许删除,即如果您发现自己有记账,请使用更简单的算法,更简单的规则或智能指针,例如{ {1}}或std::shared_ptr
:
std::unique_ptr
答案 2 :(得分:2)
在有人删除指针后,您无法假设指针将设置为NULL。 embarcadero C ++ Builder XE就是这种情况。之后它可能会设置为NULL但不要使用不允许您的代码再次删除它的事实。
答案 3 :(得分:0)
你问:“在某一点上,我想在检查它是否有效或已被某人解除分配后取消分配该指针。”
在C / C ++中有没有可移植的方法来检查&gt;裸指针&lt;是否有效。而已。在那里结束了故事。 你不能这样做。再次:只有你使用裸或C风格的指针。还有其他类型的指针没有这个问题,所以为什么不使用它们呢!
现在问题变成了:为什么你坚持你应该使用裸指针?不要使用裸指针,适当地使用std::shared_ptr
和std::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
。 weak_ptr
不会阻止对象被破坏,因此它不会出现循环引用问题。它是安全的,因为它悬挂时你不能使用它。它会断言或向您显示空指针,从而导致立即发生段错误。使用悬空指针更糟糕,因为它们似乎经常起作用。
这实际上是weak_ptr
的主要好处:使用裸C风格的指针,你永远不会知道是否有人删除了这个对象。 weak_ptr
知道最后shared_ptr
何时超出范围,并且会阻止您使用该对象。您甚至可以询问它是否仍然有效:如果对象被删除,expired()
方法将返回true
。
答案 4 :(得分:-1)
你永远不应该使用delete this
。由于两个原因,析构函数正在删除内存并使您有机会整理(释放OS资源,删除对象创建的对象中的任何指针)。其次,对象可能在堆栈中。