简体   繁体   English

异常对象的静态类型

[英]Static type of the exception object

I read the following from C++ Primer (5th edition, Section 18.1.1): "When we throw an expression, the static, compile-time type of that expression determines the type of the exception object." 我从C ++ Primer(第5版,第18.1.1节)中读到以下内容:“当我们抛出一个表达式时,该表达式的静态编译时类型决定了异常对象的类型。” So I tried the following code: 所以我尝试了以下代码:

#include <iostream>

class Base{
  public:
  virtual void print(std::ostream& os){os << "Base\n";}
};

class Derived: public Base{
  public:
  void print(std::ostream& os){os << "Derived\n";}
};

int main(){
  try{
    Derived d;
    Base &b = d;
    b.print(std::cout); //line 1
    throw b;
  }
  catch(Base& c){
    c.print(std::cout); //line 2
  }
return 0;
}

which gives me the following output: 这给了我以下输出:

Derived
Base

I think I understand why this output is expected: at line 1, we have dynamic binding. 我想我理解为什么这个输出是预期的:在第1行,我们有动态绑定。 Now when we throw b, it is based on the static type of b, which means both the static type and the dynamic type of c is Base&, and therefore we see the result at line 2. 现在当我们抛出b时,它基于b的静态类型,这意味着c的静态类型和动态类型都是Base&,因此我们在第2行看到结果。

However, if I were to use a pointer, instead of a reference: 但是,如果我使用指针而不是引用:

 int main(){
  try{
    Derived d;
    Base *b = &d;
    b->print(std::cout); //line 1
    throw b;
  }
  catch(Base* c){
    c->print(std::cout); //line 2
  }
return 0;
}

the output now becomes: 输出现在变为:

Derived
Derived

which seems to imply that the static type of c is Base*, but the dynamic type of c is Derived*, why? 这似乎意味着c的静态类型是Base *,但c的动态类型是Derived *,为什么呢? Shouldn't both the static and the dynamic types of c be Base*? 不应该是静态和动态类型的c都是Base *?

When we throw an expression, the static, compile-time type of that expression determines the type of the exception object 当我们抛出一个表达式时,该表达式的静态编译时类型决定了异常对象的类型

The above is entirely true. 以上是完全正确的。 What you forget, is that pointers are objects too. 你忘了,指针也是对象。 And when you throw a pointer, that's your exception object . 当你抛出一个指针时, 那就是你的异常对象

The object you should have dynamically 1 allocated is still pointed to by that Base* pointer. 对象应具有动态分配1通过仍指出, Base*指针。 And no slicing occurs on it, because there is no attempt to copy it. 并且没有切片发生,因为没有尝试复制它。 As such, dynamic dispatch via-pointer accesses a Derived object, and that object will use the overriding function. 因此,动态分派via-pointer访问Derived对象,该对象将使用覆盖函数。

This "discrepancy" is why it is usually best to construct the exception object in the throw expression itself. 这种“差异”是为什么通常最好在throw表达式本身中构造异常对象。


1 That pointer points to a local object, you did a big no no there and got yourself a dangling pointer. 1那个指针指向一个本地对象,你做了一个很大的不,没有那里,并给自己一个悬垂的指针。

In first case you are throwing a fresh instance of Base class invoking a copy constructor because you are passing a reference to Base into throw operator. 在第一种情况下,您将抛出一个新的Base类实例来调用复制构造函数,因为您将对Base的引用传递给throw运算符。

In second case you are throwing a pointer to a stack-allocated object of type Derived that goes out of scope when exception is thrown so then you capture and then dereference a dangling pointer causing Undefined Behavior. 在第二种情况下,您抛出指向类型为Derived的堆栈分配对象的指针,该对象在抛出异常时超出范围,因此您捕获然后取消引用导致未定义行为的悬空指针。

First scenario 第一种情况

I think that if you add some prints to your classes, you could see a clearer picture: 我认为如果你在课堂上添加一些印刷品,你会看到更清晰的画面:

struct Base {
    Base() { std::cout << "Base c'tor\n"; }
    Base(const Base &) { std::cout << "Base copy c'tor\n"; }

    virtual void print(std::ostream& os) { std::cout << "Base print\n"; }
};

struct Derived: public Base {
    Derived() { std::cout << "Derived c'tor\n"; }
    Derived(const Derived &) { std::cout << "Derived copy c'tor\n"; }

    virtual void print(std::ostream& os) { std::cout << "Derived print\n"; }
};

And the output is: 输出是:

Base c'tor
Derived c'tor
Derived print
throwing // Printed right before `throw b;` in main()
Base copy c'tor
Base print

As you can see, when calling throw b; 如你所见,当调用throw b; there is a copy construction of a different temporary Base object for the exception. 有一个异常的不同临时Base对象的副本构造。 From cppreference.com : 来自cppreference.com

First, copy-initializes the exception object from expression 首先,从表达式复制初始化异常对象

This copy-initialization slices the object, as if you assigned Base c = b 此复制初始化会对对象进行切片 ,就像您指定了Base c = b

Second scenario 第二种情况

First, you are throwing a pointer to a local object, causing undefined behavior , please avoid that by all means! 首先,您正在抛出一个指向本地对象的指针,导致未定义的行为 ,请务必避免这种情况!

Let's say you fix that, and you throw a dynamically allocated pointer, it works since you are throwing a pointer, which doesn't affect the object and preserves dynamic type information. 假设您修复了这个问题,并且抛出一个动态分配的指针,它会起作用,因为您抛出的指针不会影响对象并保留动态类型信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM