我正在阅读C ++中的智能指针,并看到这个例子给出了如果使用普通指针,智能指针如何处理“悬空指针”问题。附:我知道auto_ptr已经被C ++ 2011弃用了,但它仍然有unique_ptr或者它的一些等价物。
悬空指针。常规指针的常见缺陷是悬空指针:指向已删除对象的指针。以下代码(例如jsut)说明了这种情况:
MyClass* p(new MyClass);//Assume we have a MyClass definition available
MyClass* q = p;
delete p;
p->DoSomething();
p = NULL; // p is not dangling anymore
q->DoSomething(); // Don't.. q is still dangling!
使用auto_ptr,这可以通过在复制时将其指针设置为NULL来解决(复制构造函数实现如下):
template <class T>
auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr<T>& rhs)
{
if (this != &rhs) {
delete ptr;
ptr = rhs.ptr;
rhs.ptr = NULL;
}
return *this;
}
现在如果我们使用auto_ptr具有相同的代码,如下所示:
auto_ptr<MyClass> p(new MyClass);
auto_ptr<MyClass> q = p;
delete p;
p->DoSomething();
p = NULL;
q->DoSomething();
我在aqbove复制构造函数中得到了逻辑但是如何使rhs.ptr = NULL有帮助。我的意思是如果稍后一个解引用此指针(上面的代码示例中的q),它将崩溃,因为它由智能指针复制构造函数使其为NULL。
如果它是一个正常的指针而发生了什么,我们会取消引用一个悬空指针?一些未定义的行为可能。但是如何使用auto_ptr帮助
答案 0 :(得分:9)
智能指针不一定会保护你,如果你取消引用它们而不先检查它们;他们给你的是保证你在解除引用之前可以检查它们是否有效。
悬空指针的问题在于它没有指向任何有效的东西,但没有办法说出来:
Thing * p = new Thing;
Thing * q = p;
if (p) p->DoSomething(); // OK: checks p, calls function
if (q) q->DoSomething(); // OK: checks q, calls function
delete p;
p = nullptr;
if (p) p->DoSomething(); // OK: checks p, does nothing
if (q) q->DoSomething(); // BOOM! Undefined behaviour
使用智能指针,你永远不会有这种情况;指针有效,或者在解除引用之前可以告诉它无效:
auto_ptr<Thing> p(new Thing);
auto_ptr<Thing> q = p;
if (p) p->DoSomething(); // OK: checks p, does nothing
if (q) q->DoSomething(); // OK: checks q, calls function
p.reset(); // does nothing
if (p) p->DoSomething(); // OK: checks p, does nothing
if (q) q->DoSomething(); // OK: checks q, calls function
q.reset(); // deletes the object
if (p) p->DoSomething(); // OK: checks p, does nothing
if (q) q->DoSomething(); // OK: checks q, does nothing
答案 1 :(得分:3)
使用auto_ptr
,您可以专门测试NULL
以确保指针实际指向某个东西。通过复制构造函数和operator=
方法复制指针值的所有权可确保如果指针的所有权被“移动”,则指针实际上将为NULL
。最后,当auto_ptr
超出范围时,如果内部指针未设置为NULL
(即,所有权尚未放弃),它将为其拥有的指针释放内存。
另一方面,使用裸指针,您无法确定指针是悬空还是“拥有”指针,除非您务必确保在删除后将指针设置为NULL
等等...但手动执行此操作并非异常安全。另一方面,auto_ptr
的接口使指针的“所有权”显式化,并使动态内存分配异常安全。
答案 2 :(得分:3)
首先,它会阻止您的代码编译。声明delete p
无效,因为您无法删除auto_ptr
。将地址存储在智能指针中时,您不应再在其上调用delete
;任何对delete
的调用都会立即成为仔细检查代码的原因 - 它只是看起来不对。
删除delete
语句后,您很快就会遇到运行时错误。在使用q
初始化p
后,p
包含空指针。因此,p->DoSomething()
在空指针上调用该方法,并且 应该比原始代码更快地导致崩溃,在原始代码中,您在一个陈旧的无效指针上调用方法。 (对空指针调用方法会导致未定义的行为。典型的结果是崩溃,例如访问冲突或分段错误。无效指针上的调用方法也是未定义的,但通常会导致更微妙的错误,例如丢弃的数据或错误的计算,而不是崩溃。)
答案 3 :(得分:2)
首先,该示例是错误的,因为它在删除p后调用p->DoSomething()
。其次,当使用auto_ptr时,你不会直接调用delete,这是由auto_ptr的析构函数处理的。第三,auto_ptr的意思是它不允许指针的多个副本,所以不可能有一个悬空指针。空指针异常优于悬空指针,因为行为已定义。
答案 4 :(得分:1)
使用shared_ptr
,您永远不会手动删除它正在管理的内存;当最后一次引用超出范围时,它会自动删除。这使得悬挂指针几乎不可能,除非你手动reset()
它。