Предупреждение C4533: почему Goto пропускает инициализацию переменной? - PullRequest
2 голосов
/ 12 ноября 2011

Я получаю:

предупреждение C4533: инициализация 'b' пропускается goto FreeDC.

Но если код попадает на метку 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;
}

Ответы [ 5 ]

4 голосов
/ 12 ноября 2011
Конструктор

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 голос
/ 12 ноября 2011

Рассмотрим меньший, тривиальный тестовый пример:

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(), который вызывает delete p;. Но b.p никогда не инициализировался, так кто знает, что будет делать эта строка?

В этих случаях я считаю, что код на самом деле плохо сформирован, и компилятор должен его отклонить. Похоже, что вместо того, чтобы прямо отвергать его, компилятор решил предупредить вас о возможных проблемах, чтобы вы могли сами решить, есть ли проблемы.

1 голос
/ 12 ноября 2011

Вы можете избежать этой проблемы, введя подходящую локальную область видимости, которая будет пропущена 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 полностью пропускает.

1 голос
/ 12 ноября 2011

Вы не должны пропускать инициализацию объекта с помощью 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).

1 голос
/ 12 ноября 2011

Честно говоря, я не знаю, но почему вы используете 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.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...