我得到了:
警告C4533:goto FreeDC跳过了'b'的初始化。
但如果代码到达FreeDC
中的标签WM_CREATE
,则“b
”未初始化。如果在那种情况下没有初始化,它的初始化如何可以滑雪。我只是不明白警告。
#include <windows.h>
class A
{
int i;
public:
A() {};
A(int i) : i(i) {}
};
LRESULT CALLBACK WndProc(HWND, UINT, UINT, LONG);
HINSTANCE ghInstance;
/************************************************************************************************************************
WinMain(hInstance, hPrevInstance, pszCmdLine, nCmdShow)
************************************************************************************************************************/
int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pszCmdLine, int nCmdShow)
{
ghInstance = hInstance;
WNDCLASSEX wndclassx;
wndclassx.cbSize = sizeof(WNDCLASSEX);
wndclassx.style = CS_HREDRAW | CS_VREDRAW;
wndclassx.lpfnWndProc = WndProc;
wndclassx.cbClsExtra = 0;
wndclassx.cbWndExtra = 0;
wndclassx.hInstance = hInstance;
wndclassx.hIcon = NULL;
wndclassx.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclassx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclassx.lpszMenuName = NULL;
wndclassx.lpszClassName = L"WndProc";
wndclassx.hIconSm = NULL;
if( !RegisterClassEx(&wndclassx) ) return 0;
HWND hWnd = CreateWindow(L"WndProc", L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, SW_SHOWMAXIMIZED);
UpdateWindow(hWnd);
MSG msg;
while( GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Retorna msg.wParam
return (int)msg.wParam;
}
/************************************************************************************************************************
WndProc(hwnd, message, wParam, lParam)
************************************************************************************************************************/
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
static A a;
static int i;
switch ( message )
{
case WM_CREATE:
{
HDC hDC;
if( !(hDC = GetDC(hwnd)) ) return -1;
int iLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY);
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
lf.lfHeight = -MulDiv(11, iLogPixelsY, 72);
wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Cambria Math");
HFONT hFont;
if( !(hFont = CreateFontIndirect(&lf)) ) goto FreeDC;
hFont = (HFONT)SelectObject(hDC, hFont);
int j = 5;
i = j;
A b(2);
a = b;
return 0;
FreeDC: ReleaseDC(hwnd, hDC);
return -1;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
答案 0 :(得分:4)
b
, goto
的构造函数将不会被调用,但它仍在范围内。这在技术上是一个错误,虽然有些编译器只发出警告。
以下是一个例子:
int main() {
goto foo;
int bar = 5;
foo:
++bar; // doesn't work if goto is used - bar isn't initialized
}
看起来你似乎没有使用b
,但它的析构函数仍然被调用:
int main() {
goto foo;
A b;
foo:
b.~A(); // compiler silently adds destructor and other cleanup here
// won't work if goto is used - b isn't initialized
}
答案 1 :(得分:1)
老实说我不知道,但是当goto
声明足够时你为什么要使用if
?
if( (hFont = CreateFontIndirect(&lf)) ) {
hFont = (HFONT)SelectObject(hDC, hFont);
int j = 5;
i = j;
A b;
a = b(2);
return 0;
}
else {
FreeDC: ReleaseDC(hwnd, hDC);
return -1;
}
// break; here is unnecessary.
答案 2 :(得分:1)
您不能使用goto
或switch
[*]跳过对象的初始化(对于用户定义的类型以及int
s等基本类型,它都适用)。在您的情况下,您没有使用已跳过初始化的对象,因此最好的解决方案是通过限制b
的范围使编译器清楚。
if( !(hFont = CreateFontIndirect(&lf)) ) goto FreeDC;
hFont = (HFONT)SelectObject(hDC, hFont);
int j = 5;
i = j;
{
A b;
a = b(2);
return 0;
}
FreeDC: ReleaseDC(hwnd, hDC);
[*]所以这些都是非法的:
switch(x) {
case 1:
int y=1;
case 2:
// y not initialized if x==2
和
if (x) goto l;
int y=1;
l: // y not initialized if x!=0
如果y
是引用,常量或具有重要构造函数的用户定义对象,这一点尤为重要。
标准在6.7 / 3中表示:
可以转移到块中,但不能转移到块中 通过初始化绕过声明。一个跳跃的程序 从具有自动存储持续时间的局部变量的点开始 除非是在范围内,否则它的范围是不正确的 变量具有POD类型(3.9)并且在没有初始化程序的情况下声明 (8.5)。
答案 3 :(得分:1)
您可以通过引入goto
跳过的合适的本地范围来避免此问题:
HFONT hFont;
if( !(hFont = CreateFontIndirect(&lf)) )
{
goto FreeDC;
}
hFont = (HFONT)SelectObject(hDC, hFont);
{ // new scope; skipped entirely by goto
int j = 5;
i = j;
A b;
a = b(2);
}
return 0;
FreeDC:
ReleaseDC(hwnd, hDC);
return -1;
如果你仔细考虑C ++和范围以及自动对象生命周期,你会得出结论:goto
真的会对整个编程模型造成严重破坏。这就是为什么有许多(通常是默默暗示的)条件,你可以去哪里,而不是。通常,如果范围包含新的自动变量,则跳转到范围的中间是有问题的。我们通过引入goto
跳跃完全跳过的新的本地范围来避免这种情况。
答案 4 :(得分:1)
考虑一个较小的,微不足道的测试用例:
struct Object {
Object(int i) : i(i) { }
int i;
};
int main() {
Object a(5);
goto Label;
Object b(6);
Label:
cout << a.i << " " << b.i << endl;
}
在最后一行,a.i
显然是5
。但是b.i
的价值是多少?当创建该对象时,它应该初始化为6
,但是您明确告诉程序跳过该行。它可以是任何东西。
现在,让我们假装Object
是一种更有用的类型:
struct Object {
Object(int i) : p(new int(i)) { }
~Object() { delete p; }
//insert copy/move constructors/assignment here
int* p;
};
int main() {
Object a(5);
goto Label;
Object b(6);
Label:
cout << *a.p << endl;
}
现在,你实际上从未使用b.p
,所以看起来你跳过初始化的事实并不重要。 a.p
已正确初始化,因此输出5
,没问题。但是你从main
返回,并且析构函数开始被调用...包括调用b.~Object()
的{{1}}。但是delete p;
从未被初始化,所以谁知道那条线会做什么?
在这些情况下,我认为代码实际上是格式错误的,编译器需要拒绝它。似乎不是直接拒绝它,编译器会选择警告你可能存在的问题,以便你可以自己决定是否存在问题。