Попробуй поймать в C++ - сколько объектов создано

Рассмотрим следующий код:

#include <iostream>
#include <string>
class Box{
    public: 
        std::string show(){
            std::cout<<"Box show executed"<<std::endl;
            return "msg";
        };

        ~Box(){
            std::cout<<"Box destructor is executed"<<std::endl;
        };
};

int main(){

    try{
        Box obj;
        std::cout<<"Coming here"<<std::endl;
        throw obj;
    }
    catch(Box msg){
        std::cout<<"I have caught the exception: \n"<<msg.show()<<std::endl;
    }

}

В компиляторе GCC вывод:

Coming here
Box destructor is executed
Box show executed
I have caught the exception:
msg
Box destructor is executed
Box destructor is executed

Мои заблуждения:

  1. Распечатки операторов очень запутаны, почему печать "Box show execute" выполняется до строки "Я поймал исключение"?

  2. Вызывается три деструктора: как это возможно, когда создаются только два объекта: один объект Box в try и временный объект try, который передается в блок catch?

3 ответа

Решение

Распечатки операторов очень запутаны, почему печать "Box show execute" выполняется до строки "Я поймал исключение"?

Порядок вычисления аргументов функции / оператора не определен. Ваш код эквивалентен этому ниже:

operator<<(operator<<((std::cout << "I have caught the exception: \n"), msg.show()), std::endl);

Какой из двух приведенных ниже будет выполнен первым, не определено:

std::cout << "I have caught the exception: \n"
msg.show() // i.e. std::cout << "Box show executed" << std::endl;

В твоем случае msg.show() оценивается до первого operator << называется. Другой компилятор может создавать код с этими вызовами в другом порядке.

Вызывается три деструктора. Как это возможно, когда создаются только два объекта: один объект Box в try и временный объект try, который передается в блок catch?

Когда вы бросаете локальный объект по значению:

  1. Он копируется в специальную зарезервированную память для исключений.
  2. Оригинальный объект уничтожен (1).
  3. Должен быть найден обработчик исключений, и объект скопирован из специальной зарезервированной памяти в стек обработчика исключений.
  4. Скопированный объект удаляется из специальной памяти (2).
  5. При выходе из обработчика исключений объект удаляется из его стека (3).

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

#include <iostream>
#include <string>
class Box {
public:
    std::string show() {
        std::cout << "Box show executed - " << this << std::endl;
        return "msg";
    };

    Box() {
        std::cout << "Box constructor is executed " << this << std::endl;
    }

    Box(const Box& rhs) {
        std::cout << "Box copy constructor is executed " << this << std::endl;
    }

    ~Box() {
        std::cout << "Box destructor is executed " << this << std::endl;
    };
};

int main() {

    try {
        Box obj;
        std::cout << "Coming here" << std::endl;
        throw obj;
    }
    catch (Box msg) {
        std::cout << "I have caught the exception: \n" << msg.show() << std::endl;
    }

}

Это дает следующий вывод:

/*
Box constructor is executed 0051FB0B 
Coming here 
Box copy constructor is executed 0051FA33
Box copy constructor is executed 0051FAFF 
Box destructor is executed 0051FB0B 
Box show executed - 0051FAFF 
I have caught the exception: msg 
Box destructor is executed 0051FAFF 
Box destructor is executed 0051FA33
*/

Таким образом, вы можете увидеть ответ № 1 в том, что два новых объекта создаются во время броска и улова. Сразу после этого исходный объект разрушается, потому что теперь он находится вне области видимости блока.

Следующая часть немного сбивает с толку, потому что есть два вложенных оператора std::cout. В этом случае компилятор решил полностью разрешить выражение msg.show() до того, как произвел вывод для первой части строки "Я поймал исключение...". Таким образом, поскольку оно разрешало значение для msg.show(), msg.show() сначала выдало свою собственную строку вывода "Box show .....", именно поэтому она появилась первой. Как указал SM в своем ответе, порядок, в котором компилятор выбирает оценку вложенного std::cout, определяется реализацией, и этот порядок операций не гарантируется.

Наконец, объект, созданный в catch, уничтожается, а затем объект, созданный в try, уничтожается.

почему печатается "Показ коробки" перед строкой "Я поймал исключение"?

Потому что хочет оценить show() метод в catch раздел, он будет сначала печатать строку.

Есть три деструктора, называемые

Потому что вы ловите Box нормально, если вы используете Box& вы увидите только 2 вызова деструктора. В вашем коде объект копируется дважды, и с 1 основным объектом вам нужно 3 вызова деструктора. Если вы измените свой код следующим образом, вы увидите следующие конструкторы копирования:

#include <iostream>
#include <string>
class Box{
    public: 
        Box(){}
        Box(const Box &obj){
            std::cout <<"copy"<<std::endl;
        }

        std::string show(){
            std::cout<<"Box show executed"<<std::endl;
            return "msg";
        };

        ~Box(){
            std::cout<<"Box destructor is executed"<<std::endl;
        };
};

int main(){

    try{
        Box obj;
        std::cout<<"Coming here"<<std::endl;
        throw obj;
    }
    catch(Box msg){
        std::cout<<"I have caught the exception: \n"<<msg.show()<<std::endl;
    }    
}

и результат будет:

Coming here
copy                          // <-- for throw
Box destructor is executed
copy                          // <-- when you catch at catch(Box msg)
Box show executed
I have caught the exception: 
msg
Box destructor is executed
Box destructor is executed

Что это copyдля? один для throw obj; а другое для catch(Box msg),

Другие вопросы по тегам