异常对象

时间:2017-10-22 07:33:23

标签: c++ try-catch throw static-typing dynamic-binding

我从C ++ Primer(第5版,第18.1.1节)中读到以下内容: "当我们抛出一个表达式时,该表达式的静态编译时类型决定了异常对象的类型。"所以我尝试了以下代码:

#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;
}

给出了以下输出:

Derived
Base

我想我理解为什么会出现这样的输出:在第1行,我们有动态绑定。现在当我们抛出b时,它基于b的静态类型,这意味着c的静态类型和动态类型都是Base&amp;,因此我们在第2行看到结果。

但是,如果我使用指针而不是引用:

 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;
}

现在输出变为:

Derived
Derived

这似乎暗示c的静态类型是Base *,但c的动态类型是Derived *,为什么?不应该是静态和动态类型的c都是Base *?

3 个答案:

答案 0 :(得分:2)

  

当我们抛出一个表达式时,该表达式的静态编译时类型决定了异常对象的类型

以上是完全正确的。你忘了,指针也是对象。当你抛出指针时,那是你的异常对象

应该动态 1 分配的对象仍然由Base*指针指向。并且没有切片发生,因为没有尝试复制它。因此,动态调度via-pointer访问Derived对象,该对象将使用覆盖函数。

这种“差异”是为什么通常最好在throw表达式本身中构造异常对象。

<子> 1 那个指针指向一个本地对象,你做了一个很大的不,没有那里,并给自己一个悬垂的指针。

答案 1 :(得分:2)

在第一种情况下,您正在抛出一个新的Base类实例来调用复制构造函数,因为您将对Base的引用传递给throw运算符。

在第二种情况下,您抛出指向类型为Derived的堆栈分配对象的指针,该对象在抛出异常时超出范围,因此您捕获然后取消引用导致未定义行为的悬空指针。

答案 2 :(得分:0)

第一种情况

我认为如果你在课程中添加一些印刷品,你会看到更清晰的画面:

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"; }
};

输出是:

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

正如您所看到的,在调用throw b;时,存在异常的不同临时Base对象的副本构造。来自cppreference.com

  

首先,从表达式

复制初始化异常对象

此复制初始化切片对象,就像您分配了Base c = b

一样

第二种情况

首先,您正在抛出一个指向本地对象的指针,导致未定义的行为,请务必避免这种情况!

假设您修复了这个问题,并且您抛出一个动态分配的指针,它会起作用,因为您抛出的指针不会影响对象并保留动态类型信息。