我相信我在 Visual Studio 2012 Update 3 C++ 编译器中发现了一个有点晦涩但可怕的错误。我在使用 gtest 编写单元测试时发现了它。测试开始显示内存泄漏,在调查后问题似乎减少为编译器中的错误。
我已将问题提交给 Microsoft: https://connect.microsoft.com/VisualStudio/feedback/details/794722/parameter-dtor-not-called-when-overloaded-operator-involved-in-return
过去,我错误地将自己的错误称为“编译器错误”,其数量超出了我愿意承认的范围。所以我想我应该在这里发布问题,以防有人想尝试自己重现问题。如果我能指出这段代码中我自己的错误,那将非常有帮助!我真的希望 VC++ 编译器在下面的程序中调用析构函数实际上并不是这样。
请注意,错误行为是在禁用优化器的情况下发生的,因此这不是优化器错误。
我在 gcc 4.2.1 (i686-apple-darwin11) 中尝试了这段代码,它的行为符合预期。
以下是项目中单个源文件的代码:
#include <string>
int instance_count= 0;
class c {
public:
c( std::string s ) : m_s(s) { ++instance_count; }
c( const c& other ) : m_s(other.m_s) { ++instance_count; }
~c() {--instance_count;}
private:
std::string m_s;
};
class d {
public:
d() {}
void operator=(int) {}
};
void f( c c_ ) {
try {}
catch(...) { return d() = 5; }
}
int main( int argc, char* argv[] ) {
c instance("leak");
f(instance);
return instance_count == 1 ? 0 : -1;
}
要在 Visual Studio 2012 Update 3 中编译它:
- 文件 -> 新建 -> 项目...,选择 Win32 控制台应用程序,单击“确定”,然后单击“完成”
- 构建 -> 配置管理器 -> Active Solution Platform -> 新建...,选择 x64,单击“确定”
- 将主 .cpp 文件的内容替换为上述代码
- 将 #include "stdafx.h"添加到文件顶部或关闭预编译器 header
- 运行程序,请注意退出代码为 -1,我希望它为 0。尽管我关注的是 64 位,但这似乎在 32 位和 64 位版本中都会重现。
- 注释掉 f() 中的 try/catch block ,注意退出代码变为 0。我不明白为什么此更改会影响退出代码,因为 catch() block 甚至没有执行。<
最佳答案
看起来像是代码生成中的问题。反汇编显示函数 f 的以下内容。
带有返回语句 -
try { }
002039B8 mov byte ptr [ebp-4],1
002039BC jmp f+6Eh (02039DEh)
catch(...) { return d() = 5; }
002039BE push 5
002039C0 lea ecx,[ebp-0D5h]
002039C6 call d::d (0201474h)
002039CB mov ecx,eax
002039CD call d::operator= (0201479h)
002039D2 mov eax,2039E7h
002039D7 ret
002039D8 mov eax,2039DEh
002039DD ret
$LN4:
002039DE mov dword ptr [ebp-4],0
002039E5 jmp $LN8+0Fh (02039F6h)
$LN8:
002039E7 mov dword ptr [ebp-4],0FFFFFFFFh
002039EE lea ecx,[c_]
002039F1 call c::~c (020101Eh)
}
注意 try block 反汇编的跳转 f+6Eh(02039DEh)。这会跳转到
002039DE mov dword ptr [ebp-4],0
002039E5 jmp $LN8+0Fh (02039F6h)
这完全跳过了对析构函数的调用。还要观察的另一件事是对析构函数的调用在右大括号('}')之前。
如果我们看一下没有 return 语句的代码,
try { }
013839B8 mov byte ptr [ebp-4],1
013839BC jmp f+68h (013839D8h)
catch(...) { /*return*/ d() = 5; }
013839BE push 5
013839C0 lea ecx,[ebp-0D5h]
013839C6 call d::d (01381474h)
013839CB mov ecx,eax
013839CD call d::operator= (01381479h)
013839D2 mov eax,13839E1h
013839D7 ret
013839D8 mov dword ptr [ebp-4],0
013839DF jmp $LN8+7h (013839E8h)
$LN8:
013839E1 mov dword ptr [ebp-4],0
}
013839E8 mov dword ptr [ebp-4],0FFFFFFFFh
013839EF lea ecx,[c_]
013839F2 call c::~c (0138101Eh)
这里,对析构函数的调用是在大括号('}')之后。
关于visual-c++ - Visual Studio 2012 update 3 编译器错误 - 不调用 dtor,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17800453/