【发布时间】:2012-02-09 04:52:06
【问题描述】:
如果我在应用程序中遇到 if (!this) return; 的旧代码,这是多么严重的风险?它是一个危险的定时炸弹,需要立即在整个应用程序范围内进行搜索和销毁工作,还是更像是一种可以安静地留在原地的代码气味?
当然,我不打算编写代码来做到这一点。相反,我最近在我们的许多应用程序使用的旧核心库中发现了一些东西。
想象一个CLookupThingy 类有一个非虚拟 CThingy *CLookupThingy::Lookup( name ) 成员函数。显然,在那个牛仔时代,其中一位程序员遇到了许多从函数传递 NULL CLookupThingy *s 的崩溃,他没有修复数百个调用站点,而是悄悄地修复了 Lookup():
CThingy *CLookupThingy::Lookup( name )
{
if (!this)
{
return NULL;
}
// else do the lookup code...
}
// now the above can be used like
CLookupThingy *GetLookup()
{
if (notReady()) return NULL;
// else etc...
}
CThingy *pFoo = GetLookup()->Lookup( "foo" ); // will set pFoo to NULL without crashing
本周早些时候我发现了这颗宝石,但现在我对是否应该修复它感到矛盾。这是我们所有应用程序使用的核心库。其中一些应用程序已经交付给数百万客户,而且似乎运行良好;该代码没有崩溃或其他错误。删除查找函数中的if !this 将意味着修复数千个可能传递NULL 的呼叫站点;不可避免地会遗漏一些,引入新的错误,这些错误将在接下来的年开发中随机出现。
因此,除非绝对必要,否则我倾向于不理会它。
鉴于它在技术上是未定义的行为,if (!this) 在实践中有多危险?是否值得花费人工数周的时间来修复,还是可以指望 MSVC 和 GCC 安全返回?
我们的应用程序在 MSVC 和 GCC 上编译,并在 Windows、Ubuntu 和 MacOS 上运行。对其他平台的可移植性是无关紧要的。保证有问题的函数永远不会是虚拟的。
编辑:我正在寻找的客观答案类似于
- “当前版本的 MSVC 和 GCC 使用 ABI,其中非虚拟成员实际上是带有隐式 'this' 参数的静态变量;因此即使 'this' 为 NULL,它们也会安全地分支到函数中”或
- “即将发布的 GCC 版本将更改 ABI,因此即使是非虚拟函数也需要从类指针加载分支目标”或
- “当前的 GCC 4.5 有一个不一致的 ABI,有时它将非虚拟成员编译为带有隐式参数的直接分支,有时编译为类偏移函数指针。”
前者意味着代码很臭但不太可能破解;第二个是编译器升级后要测试的东西;后者需要立即采取行动,即使代价高昂。
显然,这是一个等待发生的潜在错误,但现在我只关心减轻我们特定编译器的风险。
【问题讨论】:
-
这太可怕了,但在你的情况下,鉴于部署基础庞大,恐怕你无能为力。
-
我认为它属于DailyWTF。
-
A call to NULL is undefined behavior in C++,所以留下来可能不是一个好主意。
-
这个问题给人的印象可能是主观的而不是建设性的——它不是!它确实有一个非常客观和明确的答案——“删除,这是一个定时炸弹”。它还有多个主观答案,具体取决于用户对旧代码库的体验......
-
在非常低级的类实现中并不少见。想想字符串类。严格的故障分析结果,您希望在哪里引发异常?你喜欢在你彻底测试过的课程中承担责任并在电话爆炸时接听电话吗?或者您是否将崩溃委托给更接近用户代码的类,从而使他明显地搞砸了一个论点?这当然是对实践产生积极影响,让它不产生任何异常是邪恶的代码。
标签: c++ visual-c++ gcc