Форум программистов, компьютерный форум, киберфорум
Наши страницы
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.75/4: Рейтинг темы: голосов - 4, средняя оценка - 4.75
lenchis001
1 / 1 / 0
Регистрация: 28.08.2014
Сообщений: 93
1

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

21.11.2014, 20:41. Просмотров 720. Ответов 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
#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 виртуальный. Спасибо.
0
Лучшие ответы (1)
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
21.11.2014, 20:41
Ответы с готовыми решениями:

Неоднозначность при множественном наследовании
Привет всем, столкнулся с проблемой при множественном наследовании классов....

компилятор находит неоднозначность при вызове round(i)
Доброго времени суток! Подскажите, компилятор находит неодназначтность в...

Использование шаблонов при наследовании, ошибка при компиляции
При изучении списков написал шаблон протестировал, все работает. После написал...

Ошибка при наследовании
Подскажите пожалуйста почему наследование класса readwrite не работает?...

Ошибка при наследовании
Здраствуйте,не могу понять где именно оштбся,возможно кто-то поможет,код...

7
zss
Модератор
Эксперт С++
7179 / 6678 / 4226
Регистрация: 18.12.2011
Сообщений: 17,622
Завершенные тесты: 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();
0
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
4608 / 2422 / 674
Регистрация: 18.10.2014
Сообщений: 4,134
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' придется написать руками.

Но это уже вопрос того, что вы хотите получить в результате. Т.е. хотите ли вы соединить эти линейки.
2
Nosey
1350 / 401 / 144
Регистрация: 22.10.2014
Сообщений: 863
Завершенные тесты: 2
21.11.2014, 22:32 4
zss, TheCalligrapher, Господа, а виртуальное наследование это слишком плохо? Али почему вы о нём не упомянули?
Спасибо
0
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 наследование не должно было убрать неоднозначность?
0
Nosey
1350 / 401 / 144
Регистрация: 22.10.2014
Сообщений: 863
Завершенные тесты: 2
21.11.2014, 23:11 6
lenchis001, Использование виртуального наследования, позволяет нам выстроить однозначную последовательность наследования классов, т.е. класс base будет в единственном экземпляре, что даст нам возможность избавиться от
C++
1
static_cast<left2 *>
Но неоднозначность в виртуальных методах всё-таки остаётся и using'om её не разрешить. Для разрешения, нужно либо оставить объявление who только в одном из следующих классов right1, left2, left1, либо же явно объявить who в result и делегировать по надобности какому-то из base классов.
0
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");
}
если наследование виртуальное, то ошибка, а если простое, то ошибка в том месте пропадает (в объявлении класса, за то появляется при попытке присвоить адрес).
0
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
4608 / 2422 / 674
Регистрация: 18.10.2014
Сообщений: 4,134
22.11.2014, 00:36 8
Лучший ответ Сообщение было отмечено lenchis001 как решение

Решение

Цитата Сообщение от 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'. Ваш код не дает однозначного ответа на это вопрос, поэтому и возникает ошибка. (Точнее, поэтому спецификация языка говорит, что такая ситуация ошибочна.)
2
22.11.2014, 00:36
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
22.11.2014, 00:36

Присвоение при наследовании
Всем доброго дня ! Подскажите пожалуйста как сделать так , что-бы при...

Конструкторы при наследовании
Описал большую иерархию классов. Надо написать конструкторы, правда уже не...

Деструктор при наследовании
Имеется 3 класса (базовый -&gt; производный -&gt; производный2) в классе базовый...


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

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru