(Сначала я попытаюсь объяснить, что происходит в данный момент, но в конце я попытаюсь написать «правильную» программу.)
Я собираюсь использовать 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&
к соответствующему типу, позволяя коду деления иметь полный доступ к обоим операндам