2009-03-06 1 views
4

Этот вопрос дает более ясность в отношении описанной проблемы here. Я сделал еще некоторое исследование и обнаружили, что стек разматывания не происходит в следующем фрагменте кода:Распаковка стека в случае структурированных исключений

class One 
{ 
public: 
    int x ; 
}; 

class Wrapper 
{ 
public: 
    Wrapper(CString csText):mcsText(csText) 
    { 
     CString csTempText; 
     csTempText.Format("Wrapper constructor :: %s\n", mcsText); 
     OutputDebugString(csTempText); 
    } 

    ~Wrapper() 
    { 
     CString csTempText; 
     csTempText.Format("Wrapper destructor :: %s\n", mcsText); 
     OutputDebugString(csTempText); 
    } 
    CString mcsText; 
}; 
class Test 
{ 
    public: 

    void notifyError() 
    { 
     try 
     { 
      int x = 10; 
     } 
     catch(...) {} 
    } 

    void OnRecvBuffer() 
    { 
     try 
     { 
      Wrapper a("AddRef");  
      One* p = NULL; 
      p->x = 10; 
     } 
     catch(...) 
     { 
      notifyError(); 
     } 
    } 
}; 



int main() 
{ 
    Test* pTest = new Test; 

    pTest->OnRecvBuffer(); 

    OutputDebugString("Test"); 
} 

Я составил этот код, используя VC6 SP5 компилятор и вывод «Упаковочный конструктор :: AddRef !!!» (т. е. деструктор объекта-обертки, который был сконструирован на стеке, не вызывается. Является ли это ожидаемым поведением? или это ошибка с компилятором VC? Могу ли я использовать некоторые флаги компилятора, чтобы в этом случае происходило раскручивание стека?

ответ

4

Если вы хотите использовать SEH, вы должны использовать функцию _set_se_translator и/компилятор EHa.

7

Стандарт C++ не дает ничего для работы в случае неопределенного поведения. Даже если MS это делает. Это специфичная для платформы вещь, поэтому будьте осторожны. Некоторые такие исключения с плавающей запятой превращаются в исключения Win32, которые вы можете попытаться поймать с помощью _set_se_translator() Проблема заключается в том, что вы можете перехватывать исключения Win32, но тогда ваш стек не будет раскручен должным образом. По крайней мере, это не то, на что вы можете поспорить свою жизнь. В этом заключается бесполезность упражнения.

Обновление: исключение намеренно проверяется на сверление стека. Вопрос в том, почему деструктор класса Wrapper не вызван. - Naveen

Если это так, не делайте этого. Есть лучшие способы бросить исключения, чем через Undefined Behavior.

например:

void OnRecvBuffer() 
{ 
    try 
    { 
     Wrapper a("AddRef");  
     throw 42; // this'll throw an exception without invoking UB 
    } 
    catch(...) 
    { 
     notifyError(); 
    } 
} 

Вы не можете разыменования указателя NULL. Вы призываете Неопределенное поведение здесь:

One* p = NULL; 
p->x = 10; 

После этой линии все ставки выключены, и вы могли убить нас всех;)

p является указателем на One объекта. Он должен содержать адрес объекта One. Вы инициализировали его до 0 - нет объекта по адресу 0. 0 не является допустимым адресом для любого объекта (это гарантируется стандартом).

+0

Исключением является намерение проверить раскрутку стека. Вопрос в том, почему деструктор класса Wrapper не вызван. – Naveen

+0

Как я уже говорил, UB - то, что происходит, когда вы вызываете UB, не определяется. Нет смысла обсуждать это. Есть и другие лучшие способы бросить исключения. – dirkgently

+1

Пример программы вызвал то, что является неопределенным поведением для стандартного C++, но вопрос в том, что Microsoft SEH (структурированная обработка исключений), а не стандартные исключения C++. – bk1e

2

Поскольку регулярное исключение C++ не обрабатывает такое исключение, вы должны использовать SEH, который ничего не знает о приложении и не расслабляется.

0

Это неопределенное поведение:

One* p = NULL; 
p->x = 10; 

На данный момент приложение является бесплатным врезаться без раскручивания стеки.
Если вы хотите, чтобы проверить стек разматывания заменить это:

throw 42; // Life the Universe and Everything thrown away 

Вы не должны динамически выделять все objcts это C++ не Java!

int main() 
{ 
    Test pTest; // Note the lack of new! 

    pTest.OnRecvBuffer(); 

    OutputDebugString("Test"); 
} 
Смежные вопросы