我正在尝试这样的代码
//A.hpp
class A{
public:
A() {}
virtual const char *message() const {return "A ERROR";}
};
//B.hpp
#include "A.hpp"
class B:public A {
public:
B() {}
const char *message() const {return "B ERROR";}
};
//main.cpp
#include "A.hpp"
#include "B.hpp"
void foo(const A& a) {
/* case 1 */ throw a; /* (or) */ /* case 2 */ throw B(); // LINE 100
}
int main() {
B b;
A &a(b);
b.message(); // OUTPUT: B ERROR
try {
foo(a);
} catch (const A& a) {
std::cout<<"EXCEPTION CALLED "<<a.message()<<std::endl;
}
return 0;
}
在这种情况下,如果我使用 情况1: 投掷// a是B b的引用; 输出:错误
案例2: 抛出B(); //创建新的B; 输出:B错误
我不明白的是,为什么两个案例之间没有一致性,
如果你一直通过引用,应该有一些一致性, 如果我在函数内部创建一个新变量,在try块中调用,那么它调用正确的虚拟方法,否则它不会.....任何人都可以告诉我控制流程....请指教。 ...
答案 0 :(得分:5)
因为在抛出之前复制了一个对象。
即使a
的参数foo
在运行时指向B
的实例,重要的是编译时类型的扔表达。因此,有效地,B
的实例将传递给A
的复制构造函数(自B
继承A
后这是合法的)和新的A
实例创建然后抛出。
复制的原因是编译器必须保证异常对象的生命周期,只要有任何catch
块可以捕获它。因此,它不会冒着堆栈对象“从堆栈边缘掉下来”的风险,或者堆栈对象被堆栈展开期间调用的一些析构函数释放。
答案 1 :(得分:2)
为了完整起见,我觉得有必要指出,就像虚拟副本(也称为克隆)解决了通过基础复制一样,虚拟(重新)投掷解决了通过基础投掷:
struct base {
virtual void
rethrow() const
{ throw *this; }
// it's also usual to make the base type abstract
// so that users can't slice, e.g. boost::exception
// it's also possible to make copying protected
};
struct derived: base {
void
rethrow() const override
{ throw *this; }
};
void
foo(base const& b)
{
// no: slices
// throw b;
b.rethrow(); // Ok
}
答案 2 :(得分:1)
投掷复制变量。你的throw a
foo实际上没有从main引用a
,它实际上会抛出a
的副本。在catch语句中,您通过引用捕获该副本。因为在foo中,a
是对A
的引用,所以副本会对对象进行切片,并且它变为A
,从而失去了它B
的事实。
答案 3 :(得分:-1)
我很难理解你究竟在问什么,但...... 首先,不要将catch的变量命名为与本地变量相同的名称,即您使用“a”两次来表示不同的东西。您可能认为通过引用捕获的项目是您传入foo的项目,如果foo()抛出B则不会是真的。为了清楚起见,将您的捕获更改为
catch (const A& ex)
{
...ex.message()...
}
我怀疑发生的事情是,在尝试之外宣布时, a 仍在范围内, a.message()调用本地变量 a < / strong>即可。如果在try中声明, a 不再在catch中的范围内,那么您通过引用捕获的 a 会调用其消息。更改catch的变量名称应该可以消除看似模棱两可的行为。