问题描述
什么是当它的不可以必要检查空一些指导?
很多继承的代码中,我的一直致力于为晚都是空的检查令人作呕。琐碎的职能null检查,对API null检查调用状态非空的回报等。在某些情况下,空检查是合理的,但在很多地方空是不是一个合理的预期。
我听说过一些争论,从你不能信任其他代码到始终编程防守到直到语言保证我一个非空值,我总是要检查。我当然有许多的这些原则在一定程度上同意,但我发现过多空检查原因通常违反这些原则的其他问题。被顽强的空检查真的值得吗?
过剩的空检查,实际上是不是更高质量的质量比较差,通常情况下,我观察到的代码。大部分的代码似乎过于关注,开发商已经失去了其他重要的品质,比如可读性,正确性或异常处理的视线零检查。特别是,我看到了很多代码忽略的std :: bad_alloc异常,但做一个空检查一个新
。
在C ++中,我明白这在一定程度上是由于提领一空指针的不可预知的行为;空取消引用是用Java,C#,Python等更优雅地处理我刚才看到警惕空检查的贫的例子还是有真东西吗?
这个问题的目的是与语言无关的,虽然我感兴趣的主要是C ++,Java和C#。
这似乎是我见过的空检查的一些例子是的过度的包括以下内容:
这个例子似乎是占非标准的编译器为C ++规范说失败的新抛出异常。除非你明确地支持不符合标准的编译器,这是否有道理?这是否的任何的感如Java或C#(或者甚至是C ++ / CLR)托管语言?
{试
为MyObject * OBJ =新MyObject来();
如果(OBJ!= NULL){
//做些什么
}其他{
//?大多数代码,我看到了登录,并在
移动//或它重复什么是在异常处理程序
}
}赶上(则为std :: bad_alloc){
//待办事项什么东西?一般 - 此代码是错误的,因为它分配
//更多的内存和很可能会失败,如写入日志文件。
}
另一个例子是内部代码时。特别是,如果它是一个小团队谁可以定义自己的发展实践,这似乎是不必要的。在某些项目或遗留代码,文档,相信未必合理...但新的代码,你或你的团队的控制,这是真的有必要吗?
如果一个方法,你可以看到并可以更新(或可谁负责开发大喊)有一个合同,它仍然需要检查空值?
// X是非负。
//返回对象或者抛出异常。
为MyObject *创建(INT X){
如果(X小于0)扔;
返回新MyObject来();
}
尝试{
为MyObject * X =创建(unknownVar);
如果(X!= NULL){
//这是空检查真的有必要吗?
}
} {赶上
//做些什么
}
在开发一个私人或内部函数,是不是真的有必要明确处理空值时合同要求非空值只要?为什么一个空检查是最好的断言?
(显然,在你的公共API,空检查是因为它被认为是不礼貌在您的用户喊至关重要不正确地使用API)
//仅供内部使用 - 非公开,不公开的API
的零件/ /输入必须非空。
//返回非负值,或-1,如果失败,
INT ParseType(字符串输入){
如果(输入== NULL)返回-1;
//做一些神奇的
返回值;
}
相比:
//仅供内部使用 - 非公开,不公开的API
部分//输入必须为非空。
//返回非负值
INT ParseType(字符串输入){
断言:(输入!= NULL输入必须是非空的。)
//做一些神奇的
返回值;
}
首先注意到,这是一个特殊的合同检查的情况:你在写代码,做什么比在一个合同记录满足运行验证等。失败意味着一些代码某处出现故障。
我总是微微半信半疑实施更普遍有用的概念的特例。因为它捕获编程错误,他们第一次跨越了一个API边界检查的合同是非常有用的。有什么特别之处空值,这意味着他们是你关心检查合同的唯一的部分?尽管如此,
在输入验证的主题:
null是Java中特殊:很多的Java API的写这样空的是,它甚至有可能传递到给定方法调用的唯一无效值。在这种情况下,空检查完全验证的输入,因此有利于合同检查的全部观点也适用。
在C ++中,在另一方面,NULL只有近2 ^ 32(2 ^ 64的新的体系结构),一个指针参数可以采取无效值之一,因为几乎所有的地址不正确类型的对象。你不能全面验证您的输入,除非你有该类型的所有对象的列表的地方。
现在的问题就是,是NULL足够常见的无效输入吃出特殊待遇(富*)( - 1)?
没有得到
与Java,领域不要自动初始化为NULL,所以垃圾未初始化的值是一样NULL作为合理。但有时C ++对象具有指针成员,其中有明确的NULL-inited,意思是我还没有一个。如果呼叫方这样做,则存在显著类的编程错误,其可以通过一个NULL检查进行诊断。一个例外可能是他们更容易比在图书馆,他们没有对源页错误进行调试。所以,如果你不介意的代码膨胀,这可能会有帮助。但是,这是你的来电者,你应该思考的,不是自己 - 这是不是防御性编码,因为它只能守与NULL,不反对(富*)( - 1)
如果NULL是无效的输入,你可以考虑通过引用而不是指针取参数,但非const引用参数很多编码风格不赞成。如果调用者传递你* fooptr,其中fooptr为NULL,则它做任何人好反正。你现在要做的就是获得更多一点儿的文件到函数签名,希望您来电者更容易想到嗯,可能fooptr为空在这里?当他们有明确取消对它的引用,比,如果他们只是把它传递给你作为一个指针。这只是到目前为止,但至于它去它可能会有所帮助。
我不知道C#,但据我所知,这是像Java在引用保证有有效的值(在安全的代码,至少),但不同于中的Java,不是所有类型的具有一个NULL值。所以我猜null检查很少有值得:如果您在安全的代码是那么就不要使用可空类型,除非null是一个有效的输入,如果在不安全代码是那么同样的道理也适用作为在C ++
在输出验证的主题:
有一个类似的问题出现了:在Java中,你可以全面验证通过了解其类型的输出,并且该值不为空。在C ++中,你不能完全验证使用NULL检查输出 - 所有你知道该函数返回一个指向它自己的堆栈的对象刚刚被解开。但是,如果NULL是由于通常由被叫用户代码的作者使用,那么检查这将有助于构建一个共同的无效退货
在所有情况下:
使用断言,而不是真正的代码,检查合同,在可能的情况 - 一旦你的应用工作,你可能不希望每一个被叫方检查其所有输入的代码膨胀,并每个来电者检查其返回值。
在编写代码的情况下,这是移植到非标准C ++实现,则不是代码,其中检查零问题同时也捕捉异常,我可能会具有这样的功能:
模板< typename的T>
静态内嵌无效nullcheck(T * PTR){
#如果PLATFORM_TRAITS_NEW_RETURNS_NULL
如果(PTR == NULL)抛出则为std :: bad_alloc();
#ENDIF
}
然后为你做的事情列表中的一个移植到新系统时,你正确地定义PLATFORM_TRAITS_NEW_RETURNS_NULL(也许其他一些PLATFORM_TRAITS)。很明显,你可以写一个标题它会为所有你知道的编译器。如果有人需要你的代码,并编译它,你一无所知,他们根本是在自己的比这更大的原因非标准的C ++实现,那么他们就必须自己做。
What are some guidelines for when it is not necessary to check for a null?
A lot of the inherited code I've been working on as of late has null-checks ad nauseam. Null checks on trivial functions, null checks on API calls that state non-null returns, etc. In some cases, the null-checks are reasonable, but in many places a null is not a reasonable expectation.
I've heard a number of arguments ranging from "You can't trust other code" to "ALWAYS program defensively" to "Until the language guarantees me a non-null value, I'm always gonna check." I certainly agree with many of those principles up to a point, but I've found excessive null-checking causes other problems that usually violate those tenets. Is the tenacious null checking really worth it?
Frequently, I've observed codes with excess null checking to actually be of poorer quality, not of higher quality. Much of the code seems to be so focused on null-checks that the developer has lost sight of other important qualities, such as readability, correctness, or exception handling. In particular, I see a lot of code ignore the std::bad_alloc exception, but do a null-check on a new
.
In C++, I understand this to some extent due to the unpredictable behavior of dereferencing a null pointer; null dereference is handled more gracefully in Java, C#, Python, etc. Have I just seen poor-examples of vigilant null-checking or is there really something to this?
This question is intended to be language agnostic, though I am mainly interested in C++, Java, and C#.
Some examples of null-checking that I've seen that seem to be excessive include the following:
This example seems to be accounting for non-standard compilers as C++ spec says a failed new throws an exception. Unless you are explicitly supporting non-compliant compilers, does this make sense? Does this make any sense in a managed language like Java or C# (or even C++/CLR)?
try {
MyObject* obj = new MyObject();
if(obj!=NULL) {
//do something
} else {
//??? most code I see has log-it and move on
//or it repeats what's in the exception handler
}
} catch(std::bad_alloc) {
//Do something? normally--this code is wrong as it allocates
//more memory and will likely fail, such as writing to a log file.
}
Another example is when working on internal code. Particularly, if it's a small team who can define their own development practices, this seems unnecessary. On some projects or legacy code, trusting documentation may not be reasonable... but for new code that you or your team controls, is this really necessary?
If a method, which you can see and can update (or can yell at the developer who is responsible) has a contract, is it still necessary to check for nulls?
//X is non-negative.
//Returns an object or throws exception.
MyObject* create(int x) {
if(x<0) throw;
return new MyObject();
}
try {
MyObject* x = create(unknownVar);
if(x!=null) {
//is this null check really necessary?
}
} catch {
//do something
}
When developing a private or otherwise internal function, is it really necessary to explicitly handle a null when the contract calls for non-null values only? Why would a null-check be preferable to an assert?
(obviously, on your public API, null-checks are vital as it's considered impolite to yell at your users for incorrectly using the API)
//Internal use only--non-public, not part of public API
//input must be non-null.
//returns non-negative value, or -1 if failed
int ParseType(String input) {
if(input==null) return -1;
//do something magic
return value;
}
Compared to:
//Internal use only--non-public, not part of public API
//input must be non-null.
//returns non-negative value
int ParseType(String input) {
assert(input!=null : "Input must be non-null.");
//do something magic
return value;
}
First note that this a special case of contract-checking: you're writing code that does nothing other than validate at runtime that a documented contract is met. Failure means that some code somewhere is faulty.
I'm always slightly dubious about implementing special cases of a more generally useful concept. Contract checking is useful because it catches programming errors the first time they cross an API boundary. What's so special about nulls that means they're the only part of the contract you care to check? Still,
On the subject of input validation:
null is special in Java: a lot of Java APIs are written such that null is the only invalid value that it's even possible to pass into a given method call. In such cases a null check "fully validates" the input, so the full argument in favour of contract checking applies.
In C++, on the other hand, NULL is only one of nearly 2^32 (2^64 on newer architectures) invalid values that a pointer parameter could take, since almost all addresses are not of objects of the correct type. You can't "fully validate" your input unless you have a list somewhere of all objects of that type.
The question then becomes, is NULL a sufficiently common invalid input to get special treatment that (foo *)(-1)
doesn't get?
Unlike Java, fields don't get auto-initialized to NULL, so a garbage uninitialized value is just as plausible as NULL. But sometimes C++ objects have pointer members which are explicitly NULL-inited, meaning "I don't have one yet". If your caller does this, then there is a significant class of programming errors which can be diagnosed by a NULL check. An exception may be easier for them to debug than a page fault in a library they don't have the source for. So if you don't mind the code bloat, it might be helpful. But it's your caller you should be thinking of, not yourself - this isn't defensive coding, because it only 'defends' against NULL, not against (foo *)(-1).
If NULL isn't a valid input, you could consider taking the parameter by reference rather than pointer, but a lot of coding styles disapprove of non-const reference parameters. And if the caller passes you *fooptr, where fooptr is NULL, then it has done nobody any good anyway. What you're trying to do is squeeze a bit more documentation into the function signature, in the hope that your caller is more likely to think "hmm, might fooptr be null here?" when they have to explicitly dereference it, than if they just pass it to you as a pointer. It only goes so far, but as far as it goes it might help.
I don't know C#, but I understand that it's like Java in that references are guaranteed to have valid values (in safe code, at least), but unlike Java in that not all types have a NULL value. So I'd guess that null checks there are rarely worth it: if you're in safe code then don't use a nullable type unless null is a valid input, and if you're in unsafe code then the same reasoning applies as in C++.
On the subject of output validation:
A similar issue arises: in Java you can "fully validate" the output by knowing its type, and that the value isn't null. In C++, you can't "fully validate" the output with a NULL check - for all you know the function returned a pointer to an object on its own stack which has just been unwound. But if NULL is a common invalid return due to the constructs typically used by the author of the callee code, then checking it will help.
In all cases:
Use assertions rather than "real code" to check contracts where possible - once your app is working, you probably don't want the code bloat of every callee checking all its inputs, and every caller checking its return values.
In the case of writing code which is portable to non-standard C++ implementations, then instead of the code in the question which checks for null and also catches the exception, I'd probably have a function like this:
template<typename T>
static inline void nullcheck(T *ptr) {
#if PLATFORM_TRAITS_NEW_RETURNS_NULL
if (ptr == NULL) throw std::bad_alloc();
#endif
}
Then as one of the list of things you do when porting to a new system, you define PLATFORM_TRAITS_NEW_RETURNS_NULL (and maybe some other PLATFORM_TRAITS) correctly. Obviously you can write a header which does this for all the compilers you know about. If someone takes your code and compiles it on a non-standard C++ implementation that you know nothing about, they're fundamentally on their own for bigger reasons than this, so they'll have to do it themselves.
这篇关于多少空的检查就够了?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!