C++ 中的安全指针取消引用
Safe pointer dereferencing in C++
在我们的代码库中,我们有很多这样的结构:
auto* pObj = getObjectThatMayVeryRarelyBeNull();
if (!pObj) throw std::runtime_error("Ooops!");
// Use pObj->(...)
在 99.99% 的情况下,不会触发此检查。我正在考虑以下解决方案:
auto& obj = deref_or_throw(getObjectThatMayVeryRarelyBeNull());
// Use obj.(...)
其中deref_or_throw
声明如下:
template<class T> T& deref_or_throw(T* p) {
if (p == nullptr) { throw std::invalid_argument("Argument is null!"); }
return *p;
}
该代码更清晰并且可以按我的需要工作。
问题是:我是在重新发明轮子吗?标准或提升中是否有一些相关的解决方案?或者您对解决方案有什么意见?
PS。相关问题(没有令人满意的答案):Is there a C++ equivalent of a NullPointerException
您可以并且可能应该在设计时考虑 null
个案例。例如,在您的问题域中,class 方法何时对 return null
有意义?什么时候使用 null
个参数调用函数才有意义?
从广义上讲,尽可能从代码中删除 null
引用是有益的。换句话说,您可以编程为 "this will not be null here... or there" 的不变量。这有很多好处。您不必通过在 deref_or_throw
中包装方法来混淆代码。您可能会从代码中获得更多语义,因为,事物 null
在现实世界中的出现频率是多少?如果您使用异常而不是 null
值来指示错误,您的代码可能更具可读性。最后,您减少了错误检查的需要,也降低了 运行 时间错误(可怕的空指针取消引用)的风险。
如果您的系统在设计时没有考虑 null
个案例,我会说最好不要管它,不要 疯狂地用 [= 包装所有东西14=]。也许采取敏捷方法。在编码时,检查 class 对象作为服务提供的合同。这些合同多久可以合理预期 return null
?在这些情况下 null
的语义值是什么?
您可能会在您的系统中识别出合理预期 return null
的 classes。对于这些 classes,null
可能是有效的业务逻辑,而不是指示实施错误的边缘案例。对于可能预期 return null
的 classes,额外的检查可能是值得的。从这个意义上说,检查 null
感觉更像是业务逻辑,而不是低级实现细节。不过,总的来说,系统地将所有指针遵循包装在 deref_or_throw
中可能是一种治愈方法,它本身就是一种毒药。
有两种方法可以处理 "rare case of null-pointer" 问题。首先,它是提议的异常解决方案。为此,deref_or_throw
方法是一件好事,尽管我更愿意抛出 runtime_error
,而不是 invalid_argument
异常。如果您愿意,也可以考虑将其命名为 NullPointerException
。
但是,如果 ptr != nullptr
情况实际上是算法的先决条件,您应该尽力在这种非空情况下实现 100% 安全。在这种情况下,assert
是合适的。
assert
的优点:
- 在发布模式下运行时无成本
- 让开发人员crystal清楚,他有责任避免这种情况发生。
assert
的缺点:
- 发布模式下的潜在未定义行为
您还应该考虑编写一个方法 getObjectThatMayNeverBeNullButDoingTheSameAsGetObjectThatMayVeryRarelyBeNull()
:保证 return 一个指向对象的非空指针的方法,该对象在其他方面完全等同于您的原始方法。但是,如果你能多走一步,我强烈建议不要 return 使用原始指针。按值拥抱 C++11 和 return 对象 :-)
在我们的代码库中,我们有很多这样的结构:
auto* pObj = getObjectThatMayVeryRarelyBeNull();
if (!pObj) throw std::runtime_error("Ooops!");
// Use pObj->(...)
在 99.99% 的情况下,不会触发此检查。我正在考虑以下解决方案:
auto& obj = deref_or_throw(getObjectThatMayVeryRarelyBeNull());
// Use obj.(...)
其中deref_or_throw
声明如下:
template<class T> T& deref_or_throw(T* p) {
if (p == nullptr) { throw std::invalid_argument("Argument is null!"); }
return *p;
}
该代码更清晰并且可以按我的需要工作。
问题是:我是在重新发明轮子吗?标准或提升中是否有一些相关的解决方案?或者您对解决方案有什么意见?
PS。相关问题(没有令人满意的答案):Is there a C++ equivalent of a NullPointerException
您可以并且可能应该在设计时考虑 null
个案例。例如,在您的问题域中,class 方法何时对 return null
有意义?什么时候使用 null
个参数调用函数才有意义?
从广义上讲,尽可能从代码中删除 null
引用是有益的。换句话说,您可以编程为 "this will not be null here... or there" 的不变量。这有很多好处。您不必通过在 deref_or_throw
中包装方法来混淆代码。您可能会从代码中获得更多语义,因为,事物 null
在现实世界中的出现频率是多少?如果您使用异常而不是 null
值来指示错误,您的代码可能更具可读性。最后,您减少了错误检查的需要,也降低了 运行 时间错误(可怕的空指针取消引用)的风险。
如果您的系统在设计时没有考虑 null
个案例,我会说最好不要管它,不要 疯狂地用 [= 包装所有东西14=]。也许采取敏捷方法。在编码时,检查 class 对象作为服务提供的合同。这些合同多久可以合理预期 return null
?在这些情况下 null
的语义值是什么?
您可能会在您的系统中识别出合理预期 return null
的 classes。对于这些 classes,null
可能是有效的业务逻辑,而不是指示实施错误的边缘案例。对于可能预期 return null
的 classes,额外的检查可能是值得的。从这个意义上说,检查 null
感觉更像是业务逻辑,而不是低级实现细节。不过,总的来说,系统地将所有指针遵循包装在 deref_or_throw
中可能是一种治愈方法,它本身就是一种毒药。
有两种方法可以处理 "rare case of null-pointer" 问题。首先,它是提议的异常解决方案。为此,deref_or_throw
方法是一件好事,尽管我更愿意抛出 runtime_error
,而不是 invalid_argument
异常。如果您愿意,也可以考虑将其命名为 NullPointerException
。
但是,如果 ptr != nullptr
情况实际上是算法的先决条件,您应该尽力在这种非空情况下实现 100% 安全。在这种情况下,assert
是合适的。
assert
的优点:
- 在发布模式下运行时无成本
- 让开发人员crystal清楚,他有责任避免这种情况发生。
assert
的缺点:
- 发布模式下的潜在未定义行为
您还应该考虑编写一个方法 getObjectThatMayNeverBeNullButDoingTheSameAsGetObjectThatMayVeryRarelyBeNull()
:保证 return 一个指向对象的非空指针的方法,该对象在其他方面完全等同于您的原始方法。但是,如果你能多走一步,我强烈建议不要 return 使用原始指针。按值拥抱 C++11 和 return 对象 :-)