Почему производный класс вызывает оператор базовых классов вместо своего собственного оператора - PullRequest
0 голосов
/ 07 января 2012

Что мне делать в этом примере, чтобы вызвать правильный оператор:

#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 ]

2 голосов
/ 07 января 2012

Две причины:

  • B::operator/(B&) является не переопределением для A::operator/(A&), поэтому он никогда не будет вызван в полиморфной ситуации.
  • B::operator/ принимает B& в качестве параметра, но статический тип *b равен A.
1 голос
/ 08 января 2012

(Сначала я попытаюсь объяснить, что происходит в данный момент, но в конце я попытаюсь написать «правильную» программу.)

Я собираюсь использовать x и y вместо a и b - это немного менее запутанно.

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.Поскольку operator/(A&) было virtual в A, тогда среда выполнения на самом деле будет вызывать C::operator/(A&).

Короче говоря, динамический тип y не имеет значения.Методы operator/(B&) или operator/(C&) никогда не будут вызваны.Эти методы должны быть удалены.

Здесь есть фундаментальная асимметрия.В *y / *x будет важен динамический тип y, но не динамический тип x.

Как это исправить

Перед тем, как «исправить» проблему, мы должны быть уверены, чтомы знаем, в чем вопрос.Поэтому сначала я уточню свою интерпретацию.Если это неверная интерпретация, заранее извиняюсь.

Я собираюсь предположить, что спрашивающий намеревается, что динамический тип 1060 * из x всегда будет таким же, как динамический тип из 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

Вот полная программа, которая делает это:

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

Операторы в B и C делают две вещи:

  • проверка того, что тип второго операнда соответствует ожидаемому.то есть подтвердите, что обе стороны / имеют одинаковый тип.
  • приводит A& к соответствующему типу, позволяя коду деления иметь полный доступ к обоим операндам
1 голос
/ 07 января 2012

Вы фактически не переопределяете operator/ из A, поскольку B::operator/ имеет сигнатуру метода, отличную от A::operator/ (параметр имеет тип B& вместо A&).Таким образом, ваш вызов operator/ для объекта типа A& вызовет единственное существующее operator/ для класса A, которое может быть virtual, но не переопределяется в class B.Поэтому вам нужно изменить operator/ из B на

    void operator/(A& b){std::cout << "class B"<<std::endl;}

Обратите внимание, что *b в любом случае не может использоваться в качестве аргумента для вашего B::operator/, поскольку он имееттип A&, а не типа B&.

1 голос
/ 07 января 2012

B::operator/ не является переопределением для A::operator/, потому что их параметры отличаются.

Вы можете переопределить виртуальные функции (или операторы), только если их подписи совместимы.

0 голосов
/ 07 января 2012

Операторская перегрузка разрешается во время компиляции с использованием статического типа, и существует только одна перегрузка (в классе A), которая может обрабатывать A (* b является A для компилятора)

...