在删除指针之前,我经常会看到遗留代码检查NULL
,类似于
if (NULL != pSomeObject)
{
delete pSomeObject;
pSomeObject = NULL;
}
在删除之前有没有理由检查NULL
指针?之后将指针设置为NULL
的原因是什么?
答案 0 :(得分:67)
删除空指针是完全“安全”的;它实际上相当于一个无操作。
在删除之前可能要检查null的原因是尝试删除空指针可能表示程序中存在错误。
答案 1 :(得分:35)
C ++标准保证在 delete-expression (§8.5.2.5/ 2)中使用空指针是合法的。但是,未指定是否会调用释放函数(operator delete
或operator delete[]
;§8.5.2.5/ 7,注意)。
如果使用空指针调用默认的释放函数(即由标准库提供),则调用无效(第6.6.4.4.2 / 3节)。
但如果标准库没有提供释放功能,那么未指定会发生什么 - 即当我们重载operator delete
(或operator delete[]
)时会发生什么。
一个称职的程序员会相应地处理里面的解除分配函数,而不是在调用之前,如OP的代码所示。同样,将指针设置为nullptr
/ {{1删除后仅用于非常有限的目的。有些人喜欢本着defensive programming的精神这样做:它会使程序行为在出现错误时更容易预测:删除后访问指针会导致空指针访问而不是访问随机记忆位置。尽管两个操作都是未定义的行为,但是在实践中空指针访问的行为更加可预测(它通常导致直接崩溃而不是内存损坏)。由于内存损坏特别难以调试,因此重置已删除的指针有助于调试。
- 当然这是治疗症状而不是病因(即病毒)。 您应该将重置指针视为代码气味。干净,现代的C ++代码将使内存所有权清晰且静态检查(通过使用智能指针或等效机制),从而可以证明避免这种情况。
NULL
: operator delete
是(尽管它的名字)一个可能像任何其他函数一样重载的函数。对于每个具有匹配参数的operator delete
调用,都会在内部调用此函数。 operator delete
也是如此。
在某些情况下,当您想要精确控制内存的分配时,重载operator new
(以及operator new
)是有意义的。这样做甚至都不是很难,但必须采取一些预防措施以确保正确的行为。 Scott Meyers详细描述了 Effective C ++ 。
现在,我们只想说我们要重载operator delete
的全局版本以进行调试。在我们这样做之前,请简要注意以下代码中发生的事情:
operator new
这里究竟发生了什么?以上可以粗略地翻译成以下代码:
klass* pobj = new klass;
// … use pobj.
delete pobj;
注意步骤2,我们用稍微奇怪的语法调用// 1st step: allocate memory
klass* pobj = static_cast<klass*>(operator new(sizeof(klass)));
// 2nd step: construct object in that memory, using placement new:
new (pobj) klass();
// … use pobj.
// 3rd step: call destructor on pobj:
pobj->~klass();
// 4th step: free memory
operator delete(pobj);
。这是对所谓的 placement new
的调用,它接受一个地址并在该地址构造一个对象。此运算符也可能过载。在这种情况下,它只用于调用类new
的构造函数。
现在,不用多说了,这里是运算符重载版本的代码:
klass
此代码在内部使用void* operator new(size_t size) {
// See Effective C++, Item 8 for an explanation.
if (size == 0)
size = 1;
cerr << "Allocating " << size << " bytes of memory:";
while (true) {
void* ret = custom_malloc(size);
if (ret != 0) {
cerr << " @ " << ret << endl;
return ret;
}
// Retrieve and call new handler, if available.
new_handler handler = set_new_handler(0);
set_new_handler(handler);
if (handler == 0)
throw bad_alloc();
else
(*handler)();
}
}
void operator delete(void* p) {
cerr << "Freeing pointer @ " << p << "." << endl;
custom_free(p);
}
/ malloc
的自定义实现,大多数实现也是如此。它还会创建一个调试输出。请考虑以下代码:
free
它产生了以下输出:
int main() {
int* pi = new int(42);
cout << *pi << endl;
delete pi;
}
现在,这段代码完全不同于Allocating 4 bytes of memory: @ 0x100160
42
Freeing pointer @ 0x100160.
的标准实现:它没有测试空指针!编译器不检查这个,所以上面的代码编译但是当你尝试删除空指针时,它可能会在运行时产生令人讨厌的错误。
但是,正如我之前所说,这种行为实际上是意料之外的,并且库编写者应该注意检查operator delete
中的空指针。这个版本得到了很大的改进:
operator delete
总之,虽然void operator delete(void* p) {
if (p == 0) return;
cerr << "Freeing pointer @ " << p << "." << endl;
free(p);
}
的草率实现可能需要在客户端代码中进行显式空检查,但这是非标准行为,只应在遗留支持中容忍(,如果有的话)。
答案 2 :(得分:9)
在内部删除对NULL的检查。您的测试是多余的
答案 3 :(得分:7)
删除null是一个无操作。在调用delete之前没有理由检查null。
如果指针为null,则可能需要检查null是否存在其他原因。
答案 4 :(得分:4)
根据C ++ 03 5.3.5 / 2,删除空指针是安全的。 以下引用标准:
在任一替代方案中,如果delete的操作数的值是 空指针操作无效。
答案 5 :(得分:3)
如果pSomeObject为NULL,则delete不会执行任何操作。所以不,你不必检查NULL。
我们认为在删除指针后为指针指定NULL是一个好习惯,如果一些指关节可以尝试使用指针的话。使用NULL指针稍微好于使用指向谁知道什么的指针(NULL指针将导致崩溃,指向已删除内存的指针可能不会)
答案 6 :(得分:1)
在删除之前没有理由检查NULL。 如果在代码中的某处检查是否已经通过执行NULL检查分配了某个对象,则可能需要在删除后分配NULL。一个例子是按需分配的某种缓存数据。每当清除缓存对象时,都会为指针指定NULL,因此分配对象的代码知道它需要执行分配。
答案 7 :(得分:0)
我相信之前的开发人员将其编写为“冗余”以节省几毫秒: 将指针删除后设置为NULL是一件好事,因此在删除对象后可以使用如下所示的行:
if(pSomeObject1!=NULL) pSomeObject1=NULL;
但是然后删除正在进行那种精确的比较(如果它为NULL则不执行任何操作)。为什么这两次?在调用delete之后,无论其当前值如何,您总是可以将pSomeObject分配给NULL - 但如果它已经具有该值,那么这将略微多余。
所以我的赌注是这些行的作者试图确保pSomeObject1在被删除后总是为NULL,而不会产生可能不必要的测试和分配的成本。
答案 8 :(得分:-2)
这取决于你在做什么。例如,free
的一些较早的实现如果传递NULL
指针则不会高兴。有些图书馆仍有这个问题。例如,Xlib库中的XFree
表示:
说明
XFree功能是一个 通用的Xlib例程 释放指定的数据。你必须 用它来释放任何对象 由Xlib分配,除非是备用 明确指定了函数 物体。 NULL指针不能 传递给了这个函数。
因此,考虑将NULL
指针释放为错误,您将是安全的。
答案 9 :(得分:-5)
至于我的观察,使用删除删除空指针在基于unix的机器上是安全的,可以使用PARISC和itanium。但对Linux系统来说是非常不安全的,因为这个过程会崩溃。