为什么派生类会调用基类运算符而不是自己的运算符

时间:2012-01-07 18:49:42

标签: c++ inheritance operator-keyword

在此示例中,我该怎么做才能触发正确的操作符:

#include<iostream>
using namespace std;

class A
{
public:
    virtual void operator/(A& a){cout << "class A";}
};
class B : public A
{
public:
    void operator/(B& b){cout << "class B";}
};

int main()
{
    A* a = new B;
    A* b = new B;
    *a / *b;
    return 0;
}

输出为"class A",但应为"class B" 我怎么解决这个问题?感谢。

编辑:根据答案......我应该为每个派生类做这个:

class A
{
public:
    virtual void operator/(A& a){cout << "class A";}
};
class B : public A
{
public:
    virtual void operator/(A& a) override {cout << "class B";}
};
class C : public B
{
public:
    virtual void operator/(A& a) override {cout << "class C";}
};
class D: public C
{
public:
    void operator/(A& a) override {cout << "class D";}
};
int main()
{
    A* a = new D;
    A* b = new D;
    *a / *b;
    //...
}

5 个答案:

答案 0 :(得分:2)

有两个原因:

  • B::operator/(B&) A::operator/(A&)的覆盖,因此在多态情况下永远不会被调用。
  • B::operator/B&作为参数,但*b的静态类型为A

答案 1 :(得分:1)

B::operator/不是A::operator/的覆盖,因为它们的参数不同。

如果签名兼容,则只能覆盖虚函数(或运算符)。

答案 2 :(得分:1)

您实际上并未覆盖operator/中的A,因为B::operator/具有与A::operator/不同的方法签名(参数类型为B&} A&)。因此,对operator/类型的对象调用A&会调用operator/类的唯一现有A,可能是virtual,但不是覆盖class B。因此,您需要做的是将operator/的{​​{1}}更改为

B

请注意 void operator/(A& b){std::cout << "class B"<<std::endl;} 无论如何都不能用作*b的参数,因为它的类型为B::operator/而不是A&类型。

答案 3 :(得分:1)

(首先,我将尝试解释目前正在发生的事情,但最后我会尝试写一个'正确的'程序。)

我将使用xy代替ab - 这有点不那么令人困惑。

A* x = new C;
A* y = new B;
*x / *y;

x静态类型A - 也就是变量的类型。 动态类型是变量指向的对象的实际类型,是C

y静态类型A - 也就是变量的类型。 动态类型是变量指向的对象的实际类型,是B

所以这里似乎涉及四种类型。但实际上,y的动态类型永远不会相关,我们将会看到。所以留下(最多)三种相关类型。要理解这一点,让我们重写表达式如下:

x -> operator/ (*y);

首先,编译器查看x静态类型。在这种情况下,它是A。然后它在具有适当签名的静态类型中查找方法。 “适当的签名”是什么意思?答案是方法查找忽略了动态类型的变量。 y的静态类型为A,因此选择operator/(A&)。 (我们知道y实际上是B,但是被忽略了。)最后,运行时将查看动态类型x(忽略y)。动态类型为C。由于A中的operator/(A&)virtual,因此运行时实际上会调用C::operator/(A&)

简而言之,y的动态类型无关紧要。永远不会调用方法operator/(B&)operator/(C&)。应删除这些方法。

这里有一个基本的不对称。在*y / *x中,y的动态类型是相关的,但不是动态类型的x。

如何解决

在“解决”问题之前,我们需要确定我们知道问题是什么。因此,我将首先澄清我的解释。如果这是错误的解释,请提前道歉。

我将假设提问者打算x动态类型将始终与{{1>的动态类型相同如果它们不同,那么应该有一条错误消息。例如,此代码可以使用:

y

A * x = new A;
A * y = new A;
*x / *y; // divide an instance of A by another instance of A

A * x = new B;
A * y = new B;
*x / *y; // divide an instance of B by another instance of B

A * x = new C;
A * y = new C;
*x / *y; // divide an instance of C by another instance of C

A * x = new B;
A * y = new C;
*x / *y; // should give an error

这是一个完整的程序:

A * x = new C;
A * y = new B;
*x / *y; // should give an error

B和C中的操作员做两件事:

  • 测试第二个操作数的类型是否符合预期。即确认#include <iostream> #include <typeinfo> #include <cassert> using namespace std; class A { public: virtual void operator/(A& z) { assert(typeid(z) == typeid(*this)); cout << "class A" << endl; } }; class B : public A { public: virtual void operator/(A& z) { assert(typeid(z) == typeid(*this)); cout << "class B" << endl; B& b = dynamic_cast<B&>(z); } }; class C : public B { public: virtual void operator/(A& z) { assert(typeid(z) == typeid(*this)); cout << "class C" << endl; C& c = dynamic_cast<C&>(z); } }; int main() { A* x = new C; A* y = new C; *x / *y; } 的两侧都有相同的类型。
  • /转换为适当的类型,允许除法代码对两个操作数具有完全访问权限

答案 4 :(得分:0)

使用静态类型解析编译时的运算符重载,并且只能处理A的一个重载(A类中的重载)(* b是编译器的A)