C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
бриллиантовое наследование, и интерфейсы
Георгий
Отправлено: 20.10.2004, 22:05


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



CODE
#include <iostream.h>

class base
{
public:
int a;
virtual void M(void){cout<<"asd"<<endl;};
};

class ParentA:virtual public base
{
public:
void M(void){cout<<"dfg"<<endl;};
};

class ParentB:virtual public base
{
public:
void M(void){cout<<"qwe"<<endl;};
};

class Child:virtual public ParentA, virtual public ParentB{};

class Child2:virtual public ParentA, virtual public base{};

class Child3:virtual public ParentB, virtual public base{};

class Child4:virtual public base, virtual public ParentB{};

class Child5: virtual public base, virtual public ParentA{};

void main(void)
{
Child c;
Child2 c2;
Child3 c3;
Child4 c4;
Child5 c5;

base *v[]={&c2,&c3,&c4,&c5};

//c.M();
const int len = sizeof(v) / sizeof(v[0]);
for(int i=0;i<len;++i)
 v[i]->M();
};

класс base предоставляет абстрактный интерфейс для неких действий;
ParentA — реализация интерфейса (назовём её пользовательской)
ParentB — другая реализация этого же интерфейса (назовём библиотечной)
и тут приспичело иметь объект ParentA, но с реализацией интерфейса взятым из библиотеки т.е., то что зовётся Child
вот такая задача.
решить её можно описав метод интерфейса в классе Child и в его реализации явно вызвать нужный метод родителей.
но смущает одно — библиотека (ParentB) создавалась, что бы не надо было в потомках заботиться о реализации интерфейса и оградить программиста от забот об интерфейсе (base) вообще.
что посоветуете?
olegenty
Отправлено: 21.10.2004, 06:47


Ветеран

Группа: Модератор
Сообщений: 2412



посмотри loki, может поможет функция построения иерархии классов, может — какой темплейт. ответ навскидку, но что-то слегонца подобное там было... Только без пояснений Александреску там легко заблудиться.
Asher
Отправлено: 21.10.2004, 09:01


Мастер участка

Группа: Модератор
Сообщений: 550



Привет.
Затык здесь?
QUOTE
class Child:virtual public ParentA, virtual public ParentB{};

обычная неоднозначность при множественном наследовании.
Решается определением новой функции в производном классе, с последующим вызовом (или невызовом) необходимых функций предков.
CODE
class Child:virtual public ParentA, virtual public ParentB{
public:
 void M(void){
  ParentA::M();//Вызов метода одного предка
  ParentB::M();//Вызов метода другого предка
  cout<<"qwe+dfg"<<endl;//Что-нибудь от себя
 };
};


Если есть возможность вывести функции из virtual, то они не пересекаются, и вызвать их можно как [CODE]
v[i]->ParentA::M();
и, соотвественно,
v[i]->ParentB::M();
[CODE]

P.S. Страуструп. 3-е издание. 15.2.1. Разрешение неоднозначности.
стр 445.
Георгий
Отправлено: 21.10.2004, 19:50


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



CODE
void M(void){
 ParentA::M();//Вызов метода одного предка
};
вот как раз от такого кода и хочу избавиться.

ж***й чую, что можно что то с иерархией сделать и всё будет Ок...

похоже подъём реализации ParentA выше по иерархии, чем то место где интерфейс ParentA берёт Child, поможет.
Георгий
Отправлено: 22.10.2004, 01:01


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



действительно описание интерфейса без реализации дало мне то что хотел:
CODE
#include <iostream.h>

class base
{
public:
virtual void M(void)=0;
};

class InterfaceA
{
virtual void interfaceA(void)=0;
};

class ParentA:virtual public base
{
public:
void M(void){cout<<"ParentA"<<endl;};
void interfaceA(void){cout<<"interfaceA implementation1"<<endl;};
};

class ParentB:virtual public base
{
public:
void M(void){cout<<"ParentB"<<endl;};
};

class Child:virtual public InterfaceA, virtual public ParentB
{
public:
void interfaceA(void){cout<<"interfaceA implementation2"<<endl;};
};

class OldChild:virtual public ParentA, virtual public ParentB
{
public:
void interfaceA(void){cout<<"interfaceA implementation2"<<endl;};
};

void main(void)
{
Child c;
c.M();
c.interfaceA();

OldChild oc;
oc.M();
oc.interfaceA();
};

соответственно Child теперь то что надо, а OldChild то, что не надо.

Отредактировано Георгий — 22/10/2004, 02:04

Присоединить изображение

Присоединить изображение

olegenty
Отправлено: 22.10.2004, 07:16


Ветеран

Группа: Модератор
Сообщений: 2412



А ларчик просто открывался. smile.gif
Asher
Отправлено: 22.10.2004, 10:11


Мастер участка

Группа: Модератор
Сообщений: 550



Привет.
Что-то я не понял решения проблемы. biggrin.gif

OldChild oc; как в исходной постановке задачи вы все равно создать не сможете — а то что вы сделали — это просто чистый абстрактный класс для предоставления общего интерфейса.
Могли и не выдумывать, а просто прочесть 12.3. Абстрактные классы.
В конце главы на стр. 362.
И, более подробно, 15.2.5. Испрользование множественного наследования, 15.2.5.1. Замещение функций виртуальных базовых классов.
Георгий
Отправлено: 22.10.2004, 23:18


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



верно — это не решение, а фигня какая то.

что есть что:
base — частично реализованный интерфейс для хранения объектов. некоторые методы должны быть реализованы в потомках, например "M".

InterfaceA — абстрактный интерфейс обработки данных.

ParentA — полностью определённый объект выполняющий обработку данных по заданному алгоритму и позволяющий свои параметры сохранять/восстанавливать

и вот после реализации все этого обнаружил, что нету у меня контейнера позволяющего сохранить/загрузить множество ParentA. Контенеры так же должны быть рекурсивными — позволять хранить сами себя (по интерфейсам не отличаться от ParentA).

реализовать это решил так:
1. использовать класс ParentB, являющийся гибридом контейнера и фабрики классов, позволяющий сохранять/восстанавливать объекты с заданным общим предком.
2. ретранслировать вызовы interfaceA на элементы контейнера

результат этой деятельности зовётся OldChild
и на него компилятор ругался такими словами, что и не знал что думать biggrin.gif

конечно же спокойный разбор выявил получившийся конфликт, например интерфейсный метод "M" реализован в обоих предках и моё желание прозрачно использовать библиотечный (ParentB::M) было незамечено со стороны транслятора smile.gif

решение "в лоб" — явно указать в OldChild какой метод вызывать, мне непонравилось, хотя бы потому, что однажды узаконненый дефект конструкции ни к чему хорошему потом не приводит. вот я и игнорировал Ваши предложения переопределить в OldChild метод "M" и уже в нём вызвать правильный интерфейсным метод "M".

"решение", что привёл в своём предыдущем посте было откровенным бредом — небыло возможности работать с ParentA и Child единообразно, да и бриллиант разбился biggrin.gif

спокойно подумав на работе решил бриллиант восстановить, а ParentA сместить ниже (или если угодно выше), а на его место поставить его абстрактный интерфейс, через который моно будет работать единообразно и с ParentA и с Child.

получается вот что:
новый интерфейс IntAB — представляет интерфейс base и то, что раньше было отличной от base частью ParentA, но не имеет никаких реализаций (чтоб конфликтов, как в "Страуструп. 3-е издание. 15.2.1. Разрешение неоднозначности.
стр 445." и в "12.3. Абстрактные классы. В конце главы на стр. 362." небыло).

ParentA — банальная реализация интрфейса IntAB
и новый Child — наследующий от ParentB реализацию методов base и берущий интерфейс ParentA от IntAB.

в итоге бриллиант восстановлен, конфликтов нет и, даже, успел поиграться с получившимся монстром и убедиться, что оно работает biggrin.gif

Спасибо.

PS. а вот и рисунок:

PPS. О, да — OldChild только демонстрация как было и как делать не надо smile.gif


Отредактировано Георгий — 23/10/2004, 00:27

Присоединить изображение

Присоединить изображение


Вернуться в Вопросы программирования в C++Builder