C++ 异常处理+;如果异常方法在同一类中不起作用,则多态性不起作用

C++ 异常处理+;如果异常方法在同一类中不起作用,则多态性不起作用,c++,exception,polymorphism,virtual-functions,throw,C++,Exception,Polymorphism,Virtual Functions,Throw,我正在尝试这样的代码 //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";

我正在尝试这样的代码

//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;
}
//A.hpp
甲级{
公众:
A(){}
虚拟常量char*message()常量{返回“错误”;}
};
//水电站
#包括“A.hpp”
B类:公共A{
公众:
B(){}
常量字符*消息()常量{返回“B错误”;}
};
//main.cpp
#包括“A.hpp”
#包括“B.hpp”
void foo(施工图A&A){
/*案例1*/throw a;//*(或)*//*案例2*/throw B();//第100行
}
int main(){
B B;
A&A(b);
b、 message();//输出:b错误
试一试{
傅(甲),;
}捕获(常数A和A){

std::cout抛出副本变量。foo中的
抛出a
实际上并没有从main中抛出对
a
的引用,它实际上抛出了
a
的副本。在catch语句中,您是通过引用捕获该副本。因为在foo中,
a
是对
a
的引用,所以副本将对象切片,并且它是出现一个
A
,因此失去了它曾经是
B

的事实,因为在抛出之前复制了一个对象

即使
foo
的参数
a
在运行时指向
B
的实例,重要的是抛出表达式的编译时类型。因此,实际上,
B
的实例被传递给
a
(这是合法的,因为
B
继承
A
)和创建并抛出的新
A
实例


复制的原因是,只要有任何
catch
块可以捕获异常对象,编译器就必须保证异常对象的生存期。因此,它不能冒堆栈对象“从堆栈边缘脱落”的风险或者堆对象被堆栈展开期间调用的某个析构函数释放。

我很难准确理解您的要求,但是。。。 首先,不要将catch的变量命名为与局部变量相同的名称,也就是说,您两次使用“a”来表示不同的事物。您可能会认为通过引用捕获的项是传递到foo中的a,如果foo()抛出B,则不会是真的。为了清楚起见,请将catch更改为

catch (const A& ex)
{
 ...ex.message()...
}

我怀疑发生的情况是,当在try之外声明时,a仍在范围内,并且a.message()调用局部变量a。如果在try中声明,a不再在catch的作用域中,因此通过引用捕获的a将调用其消息。更改catch的变量名称将删除看似不明确的行为。

为了完整性,我觉得ed指出,就像虚拟复制(也称为克隆)解决了通过基复制一样,虚拟(重新)抛出解决了通过基抛出:

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
}

无论使用哪个
a
,两者都不应该显示观察到的消息。我无法从问题中准确判断在什么条件下抛出了什么。我只是在最后一句中注意到,如果在try内部或外部声明a,则会有不同的行为。因为没有理由重用变量名“a”像这样,对我来说,清理这件事似乎是明智的。不知道为什么有人认为这是个坏主意。否决票不是因为你的建议不好,(这是个好建议),而是因为这不是问题的答案。