Множественное виртуальное наследование: почему метод класса не является неоднозначным?
Я наткнулся на следующий код C++ в онлайн-тесте.
#include <iostream>
class A
{
public:
A(int n = 2) : m_n(n) {}
public:
int get_n() const { return m_n; }
void set_n(int n) { m_n = n; }
private:
int m_n;
};
class B
{
public:
B(char c = 'a') : m_c(c) {}
public:
char get_c() const { return m_c; }
void set_c(char c) { m_c = c; }
private:
char m_c;
};
class C
: virtual public A
, public B
{ };
class D
: virtual public A
, public B
{ };
class E
: public C
, public D
{ };
int main()
{
E e;
C &c = e;
D &d = e;
std::cout << c.get_c() << d.get_n();
c.set_n(3);
d.set_c('b');
std::cout << c.get_c() << d.get_n() << std::endl;
return 0;
}
Код выводит a2a3, но я не понимаю. Почему этот прогон в первую очередь не является неоднозначным для методов класса B? Также класс E практически не наследуется.
3 ответа
Поскольку c
имеет тип C&
а также d
имеет тип D&
нет никакой двусмысленности - оба C
а также D
точно один B
субобъект.
(c
а также d
буквально относятся к соответствующим подобъектам e
- они не относятся к "e
, но под другим типом ".)
Причина, по которой на выходе выводится "a2a3", а не "a2b3" или "a2a2", заключается в том, что A
наследуется практически, поэтому есть только один A
подобъект в E
и, таким образом, только один n
член.
C
а также D
у подобъектов есть один B
каждый подобъект, и d.set_c('b');
модифицирует тот в d
но не тот в c
,
Если бы вы попробовали e.get_c()
Это было бы неоднозначно.
Тем не менее C
а также D
интерфейсы содержат только один B
друг, и ничего не знают друг о друге.
Вы применяете методы C
а также D
занятия через c
а также d
ссылки, нет никакой двусмысленности. Попробуй это
std::cout << e.C::get_c() << e.C::get_n() << std::endl;
std::cout << e.D::get_c() << e.D::get_n() << std::endl;
//line below will not compile because here access 'get_c' is ambiguous
//std::cout << e.get_c() << e.get_n() << std::endl;
здесь вы получите доступ к методам C
а также D
занятия напрямую через e
пример. Теперь последняя строка неоднозначна из-за вызова e.get_c()
и вы должны указать либо C::
или же D::
Префикс для устранения неоднозначности в пользу C
или же D
методы.
std::cout << e.C::get_c() << e.get_n() << std::endl;
std::cout << e.D::get_c() << e.get_n() << std::endl;
Примечание: нет необходимости размещать префиксы перед get_n(), потому что A
это виртуальный базовый класс, разделенный между C
а также D
в наследственном дереве.