Захват исключений по ссылке

В этом учебнике по C ++ в разделе «Стандартные исключения» есть этот пример кода, который использует class, полученный из стандартного classа исключения в STL:

// standard exceptions #include  #include  using namespace std; class myexception: public exception { virtual const char* what() const throw() { return "My exception happened"; } } myex; //Declares an instance of myexception outside of the main function int main () { try { throw myex; } catch (exception& e) //My question is regarding this line of code { cout << e.what() << endl; } return 0; } 

Этот код распечатывается. My exception happened . Однако, если я удалю амперсанд, он распечатает std::exception , что и происходит, когда вы вызываете what() со стандартным classом исключений, а не с производным classом.

Веб-сайт дает следующее объяснение:

Мы поместили обработчик, который ловит объекты исключений по ссылке (обратите внимание на амперсанд и после типа), поэтому он ловит также classы, полученные из исключения, например наш myex-объект classа myexception.

myex вроде как «вызов функции catch и передача myex в качестве параметра»? Потому что в этом случае я бы предположил, что неважно, выбрасываете ли вы исключение по значению или по ссылке (это то, что амперсанд делает правильно?), Потому что вы все еще myexception exception а не exception . И из-за динамического связывания и polymorphismа или чего-то подобного, e.what() должен все же распечатывать My exception happened а не std::exception .

Это происходит из-за fragmentации объектов.

Если вы назначаете объект производного classа экземпляру базового classа (как в случае использования копии c’tor при передаче по значению) вся информация производного classа будет потеряна (нарезана).

Например,

 class Base { int baseInfo; }; class Derived : public Base { int someInfo; }; 

Тогда, если вы должны написать это:

 Derived myDerivedInstance; Base baseInstance = myDerivedInstance; 

Затем «someInfo» в myDerivedInstance в baseInstance .

Вы можете так думать об этом, но (как вызов функции) исключение передается по значению обработчику catch . Поскольку он передается по значению, он копируется в std::exception локально в обработчик catch . Он делает это с помощью std::exception s copy constructor, и в этом случае у копии больше нет личных данных, удерживаемых myexception , или производных функций. Функции выполняют то же самое. Это называется «срез объектов», и поэтому вы никогда не храните виртуальные базовые типы по значению, только указателем или ссылкой. Вы увидите то же поведение с std::vector . Содержащиеся элементы теряют все полученные данные.

В C ++ вы можете использовать семантику семантики или ссылочную семантику при передаче и улавливании объектов, копирование по умолчанию унаследовано от C. Когда вы используете семантику копирования, программа просто создает объект исключения из myexception . Поскольку исключение ничего не знает о его потомках , вы теряете определенные части объекта myexception .

Вот почему вы должны всегда использовать ссылки или указатели, когда вам нужен polymorphism.