在删除之前是否有任何理由检查NULL指针?
在删除指针之前,我经常会看到遗留代码检查NULL
,
if (NULL != pSomeObject) { delete pSomeObject; pSomeObject = NULL; }
删除它之前是否有任何理由检查NULL
指针? 之后将指针设置为NULL
的原因是什么?
删除空指针是完全“安全的”; 它实际上相当于一个没有操作。
删除之前,您可能想要检查null的原因是试图删除空指针可能表示您的程序中的错误。
这将是非常糟糕的,但由于operator delete
可能超载,一个马虎程序员可能已经省略了空指针的testing,因此要求客户端检查它。 这可能是这里的一个原因。
还有一点,在删除之后将指针明确地设置为空可能实际上取决于代码的体系结构,这恰恰是为了防止双重删除。 由于这只能在同一个指针上保存两次删除,所以这是否是一个好的devise是非常值得怀疑的。 我永远不会依靠它。
重载operator delete
的解释:
operator delete
(尽pipe它的名字)是一个可能像任何其他函数一样被重载的函数。 这个函数在内部被调用,每次调用operator delete
都有匹配的参数。 operator new
也是如此。
当你想精确地控制内存如何分配时,重载operator new
(然后也是operator delete
)在某些情况下是有意义的。 这样做甚至不是很难,但是必须采取一些预防措施来确保正确的行为。 斯科特·迈耶斯(Scott Meyers)非常详细地介绍了Effective C ++ 。
现在,让我们只是说,我们想超载operator new
版本的operator new
进行debugging。 在我们这样做之前,先简单通知下面的代码:
klass* pobj = new klass; // … use pobj. delete pobj;
这里究竟发生了什么? 那么上面的代码大概可以翻译成如下代码:
// 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);
注意步骤2,我们用稍微奇怪的语法来调用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 = 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; free(p); }
这个代码在内部使用malloc
/ free
,就像大多数默认实现一样。 它也创build一个debugging输出。 考虑下面的代码:
int main() { int* pi = new int(42); cout << *pi << endl; delete pi; }
它产生了以下输出:
Allocating 4 bytes of memory: @ 0x100160 42 Freeing pointer @ 0x100160.
现在,这个代码与标准的operator delete
实现完全不同: 它没有testing空指针! 编译器不检查,所以上面的代码编译,但它可能会在运行时,当您尝试删除空指针时,可能会产生讨厌的错误。
但是,正如我之前所说的,这种行为实际上是意料之外的,库编写者应该注意检查operator delete
中的空指针。 这个版本有了很大的改进:
void operator delete(void* p) { if (p == 0) return; cerr << "Freeing pointer @ " << p << "." << endl; free(p); }
总之,虽然operator delete
的草率执行可能需要在客户端代码中进行显式的空检查,但这是非标准行为,只能在传统支持( 如果有的话 )中容忍。
删除null是一个空操作。 在调用delete之前没有理由检查null。
如果空指针带有一些您关心的附加信息,您可能需要检查是否有其他原因。
在内部删除检查NULL。 你的testing是重复的
根据C ++ 03 5.3.5 / 2,删除空指针是安全的。 以下是引用标准:
在任何一种情况下,如果delete的操作数的值是空指针,则操作不起作用。
如果pSomeObject为NULL,删除将不会执行任何操作。 所以不,你不必检查NULL。
我们认为最好的做法是在删除指针后将NULL指定给指针,只要有些指节头可以尝试使用指针。 使用NULL指针比使用一个指向谁知道什么(NULL指针会导致崩溃,指向已删除内存的指针可能不会)
在删除之前没有理由检查NULL。 如果在代码检查的某个地方通过执行NULL检查是否已经分配了某个对象,那么在删除之后分配NULL可能是必要的。 一个例子就是某种按需分配的caching数据。 无论何时清除caching对象,都将空值分配给指针,以便分配对象的代码知道需要执行分配。
这取决于你在做什么。 例如,一些较旧的free
实现,如果它们传递了一个NULL
指针,将不会很高兴。 一些图书馆仍然有这个问题。 例如,Xlib库中的XFree
说:
描述
XFree函数是一个通用的Xlib例程,用于释放指定的数据。 您必须使用它来释放由Xlib分配的任何对象,除非为该对象明确指定了替代函数。 一个NULL指针不能被传递给这个函数。
所以考虑释放NULL
指针作为一个错误,你会很安全。
我相信以前的开发人员将其编码为“冗余”以节省一些毫秒:将指针删除时设置为NULL是一件好事,因此在删除对象后,可以使用如下所示的行:
if(pSomeObject1!=NULL) pSomeObject1=NULL;
但是,然后删除正在做这个确切的比较(如果它是NULL什么都不做)。 为什么这两次? 在调用delete之后,您始终可以将pSomeObject指定为NULL,而不pipe其当前值是什么 – 但是如果已经具有该值,则会稍微多余。
所以我敢打赌,这些行的作者试图确保pSomeObject1被删除后总是为NULL,而不会产生潜在的不必要的testing和分配的代价。
至于我的观察,删除空指针使用删除是安全的基于Unix的机器ike PARISC和itanium。 但是对于Linux系统来说是非常不安全的,因为这个过程将会崩溃。