Множественное виртуальное наследование: почему метод класса не является неоднозначным?

Я наткнулся на следующий код 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 в наследственном дереве.

Другие вопросы по тегам