Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.73/15: Рейтинг темы: голосов - 15, средняя оценка - 4.73
1 / 1 / 0
Регистрация: 28.08.2014
Сообщений: 93

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

21.11.2014, 20:41. Показов 3045. Ответов 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)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
21.11.2014, 20:41
Ответы с готовыми решениями:

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

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

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

7
Модератор
Эксперт С++
 Аватар для zss
13773 / 10966 / 6491
Регистрация: 18.12.2011
Сообщений: 29,244
21.11.2014, 21:29
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
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12942 / 6809 / 1821
Регистрация: 18.10.2014
Сообщений: 17,231
21.11.2014, 22:21
Цитата Сообщение от 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
1379 / 406 / 144
Регистрация: 22.10.2014
Сообщений: 872
21.11.2014, 22:32
zss, TheCalligrapher, Господа, а виртуальное наследование это слишком плохо? Али почему вы о нём не упомянули?
Спасибо
0
1 / 1 / 0
Регистрация: 28.08.2014
Сообщений: 93
21.11.2014, 22:51  [ТС]
Спасибо, пол-проблемы уже решено, вот:
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
1379 / 406 / 144
Регистрация: 22.10.2014
Сообщений: 872
21.11.2014, 23:11
lenchis001, Использование виртуального наследования, позволяет нам выстроить однозначную последовательность наследования классов, т.е. класс base будет в единственном экземпляре, что даст нам возможность избавиться от
C++
1
static_cast<left2 *>
Но неоднозначность в виртуальных методах всё-таки остаётся и using'om её не разрешить. Для разрешения, нужно либо оставить объявление who только в одном из следующих классов right1, left2, left1, либо же явно объявить who в result и делегировать по надобности какому-то из base классов.
0
1 / 1 / 0
Регистрация: 28.08.2014
Сообщений: 93
21.11.2014, 23:33  [ТС]
Хорошо, а почему тогда в этом коде:
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
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12942 / 6809 / 1821
Регистрация: 18.10.2014
Сообщений: 17,231
22.11.2014, 00:36
Лучший ответ Сообщение было отмечено 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
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
22.11.2014, 00:36
Помогаю со студенческими работами здесь

Ошибка при наследовании?
Программа компилируется, но консоль не выдает информацию. Связано ли это как то с переполнением буфера? Программа небольшая укажите...

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

Ошибка при наследовании
Здраствуйте,не могу понять где именно оштбся,возможно кто-то поможет,код прилагаю //#include &quot;stdafx.h&quot; #include...

С++ течет при наследовании
Если был создан класс где его поля размещались в стеке (Foo) и унаследован классом (Foo1) который создается динамически. Как...

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


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

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Новые блоги и статьи
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip На первой гифке отладочные линии отключены, а на второй включены:. . .
SDL3 для Web (WebAssembly): Идентификация объектов на Box2D v3 - использование userData и событий коллизий
8Observer8 02.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-collision-events-sdl3-c. zip Сканируйте QR-код на мобильном и вы увидите, что появится джойстик для управления главным героем. . . .
Реалии
Hrethgir 01.03.2026
Нет, я не закончил до сих пор симулятор. Эта задача сложнее. Не получилось уйти в плавсостав, но оно и к лучшему, возможно. Точнее получалось - но сварщиком в палубную команду, а это значит, в моём. . .
Ритм жизни
kumehtar 27.02.2026
Иногда приходится жить в ритме, где дел становится всё больше, а вовлечения в происходящее — всё меньше. Плотный график не даёт вниманию закрепиться ни на одном событии. Утро начинается с быстрых,. . .
SDL3 для Web (WebAssembly): Сборка библиотек: SDL3, Box2D, FreeType, SDL3_ttf, SDL3_mixer и SDL3_image из исходников с помощью CMake и Emscripten
8Observer8 27.02.2026
Недавно вышла версия 3. 4. 2 библиотеки SDL3. На странице официальной релиза доступны исходники, готовые DLL (для x86, x64, arm64), а также библиотеки для разработки под Android, MinGW и Visual Studio. . . .
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru