Раскручивание стека в случае структурированных исключений
-
03-07-2019 - |
Вопрос
Этот вопрос дает больше ясности по описанной проблеме. здесь.Я провел еще несколько исследований и обнаружил, что раскручивание стека не происходит в следующем фрагменте кода:
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?Могу ли я использовать какие-то флаги компилятора, чтобы в этом случае происходила раскрутка стека?
Решение
Если вы хотите использовать SEH, вы должны использовать функцию _set_se_translator и параметр компилятора / EHa.
Другие советы
Стандарт C++ не дает ничего, с чем можно было бы работать в случае неопределенного поведения.Даже если MS это сделает.Это специфичная для платформы вещь, так что будьте осторожны.Некоторые такие исключения с плавающей запятой превращаются в исключения Win32, которые вы можете попытаться перехватить с помощью _set_se_translator()
.Проблема в том, что вы можете перехватить исключения Win32, но тогда ваш стек не будет правильно развернут.По крайней мере, это не то, на что вы можете поставить свою жизнь.В этом и заключается бесполезность упражнения.
Обновлять:Исключение выбрасывается намеренно, чтобы проверить раскручивание стека.Вопрос в том, почему деструктор класса Wrapper не вызывается.- Навин
Если это так – не делайте этого.Есть более эффективные способы создания исключений, чем с помощью Undefine 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 не является допустимым адресом для любого объекта (это гарантируется стандартом).
Поскольку обычное исключение C ++ не обрабатывает такого рода исключения, вы должны использовать SEH, который ничего не знает о приложении и не раскручивает.
Это неопределенное поведение:
One* p = NULL;
p->x = 10;
В этот момент приложение может завершить работу без разматывания стека.
Если вы хотите проверить раскрутку стека, замените это на:
throw 42; // Life the Universe and Everything thrown away
Вы не должны динамически размещать все ваши объекты, это C ++, а не Java!
int main()
{
Test pTest; // Note the lack of new!
pTest.OnRecvBuffer();
OutputDebugString("Test");
}