Форум программистов, компьютерный форум CyberForum.ru

Неоднозначность при наследовании - C++

Восстановить пароль Регистрация
 
lenchis001
1 / 1 / 0
Регистрация: 28.08.2014
Сообщений: 93
21.11.2014, 20:41     Неоднозначность при наследовании #1
Привет народ, думаю мой вопрос покажется вам чем-то из разряда "лучше бы делом занялся", но тех у кого останется терпение, прошу помочь мне преодолеть неоднозначность в данном коде:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include"stdafx.h"
#include"iostream"
using namespace std;
class base{
public:
    void who(){
        cout << 1;
    }
};
class left1 :public base{
public:
    void who(){
        cout << 2;
    }
};
class left2 :public left1{
public:
    void who(){
        cout << 3;
    }
};
class right1 :public base{
public:
    void who(){
        cout << 4;
    }
};
class result :public right1, public left2{
 
};
int main(){
    base *p;
    result ob;
    ob.who();
    p = &ob;
    p->who();
    system("pause");
}

а также в случае, когда метод в классе base виртуальный. Спасибо.
Лучшие ответы (1)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
21.11.2014, 20:41     Неоднозначность при наследовании
Посмотрите здесь:

C++ Ошибка при наследовании
C++ Ошибка при наследовании
C++ Ошибка при наследовании
Конструкторы при наследовании C++
С++ течет при наследовании C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
zss
Модератор
Эксперт С++
 Аватар для zss
5942 / 5547 / 1783
Регистрация: 18.12.2011
Сообщений: 14,155
Завершенные тесты: 1
21.11.2014, 21:29     Неоднозначность при наследовании #2
C++
1
2
3
4
5
result ob;
ob.right1::who();
ob.left2::who();
ob.left1::who();
ob.base::who();;
Если who объявить виртуальной, то можно такое
C++
1
2
3
4
5
base *p;
p = &ob;
dynamic_cast<left2*>(p)->who();
dynamic_cast<left1*>(p)->who();
p->base::who();
TheCalligrapher
С чаем беда...
Эксперт С++
 Аватар для TheCalligrapher
2782 / 1428 / 393
Регистрация: 18.10.2014
Сообщений: 2,622
21.11.2014, 22:21     Неоднозначность при наследовании #3
Цитата Сообщение от lenchis001 Посмотреть сообщение
преодолеть неоднозначность
Для разрешения неоднозначности в приведении типа вверх надо явно выполнить приведение к какому-нибудь более высокому типу, к рамках которого дальнейший путь наверх является уже однозначным.

Т.е.

C++
1
2
3
p = static_cast<right1 *>(&ob);
// или
p = static_cast<left2 *>(&ob);
в зависимости от того, в какой конкретно базовый подобъект типа 'base' вы хотите попасть.

Неоднозначность в вызове 'ob.who();' можно разрешить либо приведением типа объекта (как показано выше), либо указанием квалифицированного имени метода в вызове

C++
1
ob.left2::who();
Однако помните, что квалифицированное имя подавляет виртуальность вызова (если она есть). Поэтому предпочтительнее приведение типа.

Можно также указать, какой именно метод вы предпочитаете при вызове через 'result' при помощи 'using'

C++
1
2
3
4
5
6
class result :public right1, public left2{
public:
  using right1::who;
  // или
  using left2::who;
};
Цитата Сообщение от lenchis001 Посмотреть сообщение
а также в случае, когда метод в классе base виртуальный.
Когда метод является виртуальным, тогда вам придется вручную реализовать метод 'who' в классе 'result'. В противном случае линейки виртуальных функций с корнями в 'left2::left1::base' и 'right1::base' останутся несоединенными. Через одну базу 'base' будет вызываться 'left2::who', а через другую 'right1::who'. Вышеприведенный вариант с 'using' не создает final overrider для 'who' в классе 'result'. Поэтому функцию 'who' для 'result' придется написать руками.

Но это уже вопрос того, что вы хотите получить в результате. Т.е. хотите ли вы соединить эти линейки.
Nosey
 Аватар для Nosey
1184 / 351 / 102
Регистрация: 22.10.2014
Сообщений: 786
Завершенные тесты: 2
21.11.2014, 22:32     Неоднозначность при наследовании #4
zss, TheCalligrapher, Господа, а виртуальное наследование это слишком плохо? Али почему вы о нём не упомянули?
Спасибо
lenchis001
1 / 1 / 0
Регистрация: 28.08.2014
Сообщений: 93
21.11.2014, 22:51  [ТС]     Неоднозначность при наследовании #5
Спасибо, пол-проблемы уже решено, вот:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include"stdafx.h"
#include"iostream"
using namespace std;
class base{
public:
    virtual void who(){
        cout << 1;
    }
};
class left1 :virtual public base{
public:
    void who(){
        cout << 2;
    }
};
class left2 :public left1{
public:
    void who(){
        cout << 3;
    }
};
class right1 :virtual public base{
public:
    void who(){
        cout << 4;
    }
};
class result :public right1, public left2{
public:
    using right1::who;
};
int main(){
    base *p;
    result ob;
    ob.who();
    cout << '\n';
    p = static_cast<left2 *>(&ob);
    p->who();
    system("pause");
}
теперь жалуется только на result (в объявлении класса), дескать: неоднозначное наследование "void base::who(void)", но разве virtual наследование не должно было убрать неоднозначность?
Nosey
 Аватар для Nosey
1184 / 351 / 102
Регистрация: 22.10.2014
Сообщений: 786
Завершенные тесты: 2
21.11.2014, 23:11     Неоднозначность при наследовании #6
lenchis001, Использование виртуального наследования, позволяет нам выстроить однозначную последовательность наследования классов, т.е. класс base будет в единственном экземпляре, что даст нам возможность избавиться от
C++
1
static_cast<left2 *>
Но неоднозначность в виртуальных методах всё-таки остаётся и using'om её не разрешить. Для разрешения, нужно либо оставить объявление who только в одном из следующих классов right1, left2, left1, либо же явно объявить who в result и делегировать по надобности какому-то из base классов.
lenchis001
1 / 1 / 0
Регистрация: 28.08.2014
Сообщений: 93
21.11.2014, 23:33  [ТС]     Неоднозначность при наследовании #7
Хорошо, а почему тогда в этом коде:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include"stdafx.h"
#include"iostream"
using namespace std;
class base{
public:
    virtual void who(){
        cout << 1;
    }
};
class left1 :virtual public base{
public:
    void who(){
        cout << 2;
    }
};
class left2 :public left1{
public:
    void who(){
        cout << 3;
    }
};
class right1 :virtual public base{
public:
    void who(){
        cout << 4;
    }
};
class result :public right1, public left2{
public:
    using right1::who;
};
int main(){
    base *p;
    result ob;
    ob.who();
    cout << '\n';
    p = static_cast<left2 *>(&ob);
    p->who();
    system("pause");
}
если наследование виртуальное, то ошибка, а если простое, то ошибка в том месте пропадает (в объявлении класса, за то появляется при попытке присвоить адрес).
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
22.11.2014, 00:36     Неоднозначность при наследовании
Еще ссылки по теме:

компилятор находит неоднозначность при вызове round(i) C++
C++ Ошибка при наследовании?
Деструктор при наследовании C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
TheCalligrapher
С чаем беда...
Эксперт С++
 Аватар для TheCalligrapher
2782 / 1428 / 393
Регистрация: 18.10.2014
Сообщений: 2,622
22.11.2014, 00:36     Неоднозначность при наследовании #8
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Цитата Сообщение от lenchis001 Посмотреть сообщение
если наследование виртуальное,
Короткий ответ - потому что этого требуют правила языка.

Длинный ответ:

1. Когда база 'base' является невиртуальной, в вашей иерархии классов есть два корня: левый корень 'base' и правый корень 'base'. Из каждого такого корня вниз растут свои линейки (или даже подыерархии) классов-наследников, в которых как-то по своему перекрывается виртуальная функция 'who'.

В один прекрасный момент эти две линейки (левая и правая) встречаются друг с другом в классе 'result'. Эти две линейки ничего не знают и не должны знать друг о друге. Левая линейка спокойно считает класс 'result' своим наследником, и правая линейка тоже спокойно считает класс 'result' своим наследником.

Пришедшие в класс 'result' две линейки виртуальных функций 'who' тоже сосуществуют независимо, не мешая друг другу. C точки зрения каждой линейки у функции 'who' есть конкретный final overrider не всех уровнях иерархии, в том числе и в 'result'. Левая линейка считает, что для класса 'result' должна работать функция 'left2::who', а правая линейка считает, что для класса 'result' должна работать функция 'right1::who'.

В этом не никакого конфликта. Какая версия 'who' будет работать в каждом конкретном вызове зависит от того, из какой линейки (левой или правой) пришел базовый подобъект 'base', использованный в вызове.

2. Но когда база 'base' становится виртуальной, картина сильно меняется. Теперь в иерархии есть только одна единственная база 'base', один-единственный корень из которого все растет. Из этого единственного корня растут две ветки - левая и правая - которые потом снова встречаются друг с другом в классе 'result'. Пришедшие к класс 'result' линейки перекрытых функций 'who' не являются независимыми - у них общий единственный корень 'base'.

Это уже не две независимых линейки. Это одна единственная общая подыерархия. И рамках этой одной подыерархии класс 'result' не имеет однозначного final overrider для функции 'who'. Это ошибка. Компилятору надо знать конкретный final overrider для функции 'who' в классе 'result'.

===================

Если опуститься на уровень реализации, то можно описать ситуацию так:

1. Когда база 'base' является невиртуальной, в классе 'result' есть два независимых подобъекта 'base', каждый из которых содержит свой указатель на таблицу виртуальных методов (VMT). Левый подобъект 'base' указывает на "левую" VMT, правый - на "правую" VMT. В каждой VMT содержится указатель на свой метод 'who'. Они друг другу никак не мешают.

2. Когда база 'base' является виртуальной, в классе 'result' есть только один подобъект 'base', который разумеется содержит только один указатель на VMT. Так как VMT только одна, компилятор должен знать, что же в нее вписать в качестве указателя на 'who'. Ваш код не дает однозначного ответа на это вопрос, поэтому и возникает ошибка. (Точнее, поэтому спецификация языка говорит, что такая ситуация ошибочна.)
Yandex
Объявления
22.11.2014, 00:36     Неоднозначность при наследовании
Ответ Создать тему
Опции темы

Текущее время: 20:16. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru