Страницы

Поиск по вопросам

воскресенье, 29 декабря 2019 г.

Каким образом Release проглатывает исключения

#cpp #visual_cpp


Есть код следующий код. 

#pragma once
#include 
#include 

using namespace std;

class TestObject {
public:
    TestObject() : st("default") { cout << st << " is created" << endl; }
    TestObject(const string& s) : st(s) { cout << st << " is created" << endl; }
    string ToString() { return st; }
    void Print() { cout << st << "... printed itself" << endl; }
    ~TestObject() { cout << st << " is destroyed" << endl; }
private:
    string st;
public:
    static TestObject& null() {
        static TestObject to("null");
        return to;
    }
};

TestObject& test() 
{
    TestObject to("Local");
    return to;
    return TestObject::null();
}

int main() 
{
    test().Print();
    cout << endl;
    system("pause");
    return 0;
}


Если запустить этот код в режиме Debug, то возникает ошибка, что и понятно, так как
функция возвращает ссылку на локальный объект. 

Однако если запустить в режиме Release, то все работает и получается следующий вывод:

Local is created
Local is destroyed
... printed itself

Для продолжения нажмите любую клавишу . . .


Что немного странно. 

Кроме того, если вызвать test().Print(); дважды подряд, то уже на первом вызове будет
сгенерировано исключение и программа остановится. 

Я имел несчастье допустить подобную ошибку в реальном проекте, и, собрав программу
под Release, я потом очень долго гадал, почему функция возвращает пустое значение по
умолчанию, которое как я выяснил в данном примере таковым вообще не является. Под отладчиком
в Release вызывается будто бы return TestObject::null();, хотя печатается не null,
а просто пустая строка.

Объясните, пожалуйста, что именно происходит в режиме Release. 



По совету @Harry запустил следующий код:

TestObject &t = test();
TestObject::null().Print();
t.Print();


Сначала, вывелось много бреда и начали генерироваться отрывистые звуки "пи, пи, пи",
после чего VS остановила работу программы с ошибкой чтения чужой памяти, но звук прекратился,
только когда нажал "Прервать". Все последующие попытки запустить код заканчивались
остановкой программы со следующим выводом.

Local is created
Local is destroyed
null is created
null... printed itself
|√& √*Ф ·в┬│    L√& j3╪u р¤~М√& ☻Щлw р¤~`Я·k         р¤~            X√&         
┼Xпw╠вv∟    д√& ╒Шлw°$Ф  р¤~                °$Ф  р¤~

    


Ответы

Ответ 1



Вам просто не везет :) Например, адрес, по которому лежал локальный объект, оказался еще не затерт, вот и все. Но, заметьте - строка st уже уничтожена, так что выводится просто ... printed itself а не Local... printed itself Просто в режиме Release уже не выполняются никакие проверки (на то он и Release, чтоб не тратить время и ресурсы на какие-то лишние проверки) - программа считается отлаженной. А исключение при таком неопределенном поведении генерироваться вовсе не обязано. Попробуйте интереса ради вклинить еще один вызов - например, так: TestObject &t = test(); TestObject::null().Print(); t.Print(); Думаю, будет более интересно. Что именно будет - предсказать невозможно :)

Ответ 2



Ваша программа вызывает классическое неопределенное поведение. Причем не какое-то концептуальное, а совершенно реальное и практически непредсказуемое. Почему вас удивляет, что проявления этого поведения отличаются в отладочной и релизной версии кода? (У меня, например, в VS2015 отладочная версия вашего кода не выкидывает никакой ошибки, а просто "ломает" cout при вызове Print(), после чего он вообще отказывается что-либо выводить.) То, что релизный код (как ваш собственный, так и код стандартной библиотеки) будет радикально отличаться от дебажного из-за одних только оптимизаций, я надеюсь, объяснять не надо. А уж каким этот релизный код получится зависит от стольких труднопредсказуемых переменных, что пытаться искать тут какие-то детерминистские объяснения - пустая трата времени. Также не ясно, о каком "проглатывании исключения" вы ведете речь. Никаких исключений в этом коде нет.

Комментариев нет:

Отправить комментарий