通过合同编程时,函数或方法首先检查其前提是否已满足,然后才开始履行其职责,对吗?两个最重要的方式做这些检查是通过assert和exception.
你觉得哪一个更好?
请参阅此处的相关问题
在我的大多数C++项目中,我大量使用ASSERTION语句如下:
int doWonderfulThings(const int* fantasticData)
{
ASSERT(fantasticData);
if(!fantasticData)
return -1;
// ,,,
return WOW_VALUE;
}
Run Code Online (Sandbox Code Playgroud)
但TDD社区似乎喜欢这样做:
int doMoreWonderfulThings(const int* fantasticData)
{
if(!fantasticData)
return ERROR_VALUE;
// ...
return AHA_VALUE;
}
TEST(TDD_Enjoy)
{
ASSERT_EQ(ERROR_VALUE, doMoreWonderfulThings(0L));
ASSERT_EQ(AHA_VALUE, doMoreWonderfulThings("Foo"));
}
Run Code Online (Sandbox Code Playgroud)
根据我的经验,第一种方法让我删除了许多微妙的错误.但TDD方法是处理遗留代码的非常聪明的想法.
"谷歌" - 他们将"第一种方法"与"带着救生衣走在岸边,没有任何安全防护的游泳海洋"进行比较.
哪一个更好?哪一个使软件健壮?
上下文:在这个答案中,我了解到 gcc__builtin_unreachable()可能会对性能产生一些令人惊讶的影响,因为看起来如下:
if(condition) __builtin_unreachable();
Run Code Online (Sandbox Code Playgroud)
被完全剥离,只要condition能保证没有任何副作用,就可以用作优化提示。
因此,我对此的立即反应是,我应该创建以下宏,并在我通常使用的任何地方都使用它assert(),因为在 an 内诱导代码的副作用assert()首先将是一个主要错误:
// TODO: add handling of other compilers as appropriate.
#if defined(__GNUC__) && defined(NDEBUG)
#define my_assert(condition) \
if(!(condition)) __builtin_unreachable()
#else
#define my_assert(condition) assert(condition)
#endif
Run Code Online (Sandbox Code Playgroud)
从标准的角度来看,这将在正常和构建之间创建功能划分NDEBUG,您可以提出一个论据,将该宏排除在assert(). 然而,由于无论如何,在断言失败的情况下,我的代码在功能上都会死在水中,因此从行为的角度来看,它是完全等效的。
所以我的问题是:任何人都可以想出一个不这样做的理由(除了涉及大量间接的断言之外)?
在你问之前,是的,我已经检查过 gcc 的行为是在构建中无效地放弃断言NDEBUG。