4

I just came up with a problem when I learned about using Exceptions. The code is as follows.

// exception constructor
#include <iostream>  // std::cout
#include <exception> // std::exception
#include <cxxabi.h>

#define PRINT_TYPENAME(f) std::cout << abi::__cxa_demangle(typeid(f).name(), 0, 0, &status) << std::endl;

struct ooops : std::exception
{
    const char *what() const noexcept { return "Ooops!\n"; }
};

int main()
{
    ooops e;
    std::exception *p = &e;

    // demangle the typeid
    char *realname;
    int status;
    PRINT_TYPENAME(p);
    PRINT_TYPENAME(*p);

    try
    {
        throw e; // throwing copy-constructs: ooops(e)
    }
    catch (std::exception &ex)
    {
        std::cout << ex.what();
    }
    try
    {
        throw *p; // throwing copy-constructs: std::exception(*p)
    }
    catch (std::exception &ex)
    {
        std::cout << ex.what();
    }
    return 0;
}

The output is:

std::exception*
ooops
Ooops!
std::exception

It seems that *p has the type of the derived class. However, when std::exception &ex=*p, the type_id.name() of ex becomes the base class rather than the derived class.

Since *p and ex have the same type. I feel confused about why the type of ex differs between the two try-catch sentences.

Can someone tell me the reason? Thanks for the help.

pikalee
  • 95
  • 6
  • "*p and e have the same type" is incorrect assumption. *p is reference to std::exception subobject of e, throw always full copyable objects, not base class subobjects. – Öö Tiib Mar 22 '23 at 11:12

1 Answers1

5

When an exception is thrown, the exception object is copy-initialized from your throw expression. I.e., rather than std::exception &ex = *p;, it's more like you did std::exception temp = *p; and then std::exception& ex = temp;.

This results in object slicing, where the derived part of your class is "sliced off". This is why inside your catch handler, the dynamic type of ex is std::exception instead of oops.

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • But `typeid(*p).name()` still printout `oops` rather than `std::exception`. So why the copy-initialization from the throw expression is like`std::exception temp = *p;` rather than `oops temp = *p`? In other words, why does `typeid(*p).name()` printout `oops` rather than `std::exception`? – pikalee Mar 25 '23 at 08:01
  • 1
    Because the copy-initialization of the exception is based on the *static* type of the thrown object, not the *dynamic* type – TartanLlama Mar 27 '23 at 13:42