Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Классы


Автор: bel_nikita 3.11.2003, 14:43
проблема следующая:

class Base {...};

class Derived: public/private Base {...};

Возможно ли следующие наследование:

class Test: public Base, public Derived {...};

Автор: maxim1000 3.11.2003, 15:11
да

Автор: Ars 3.11.2003, 15:24
Билдер такое откомпилить не даст, да и зачем?

Автор: bel_nikita 3.11.2003, 15:36
maxim1000 что значит ДА?
подскажи как реализовать таку схему
такое у меня не компилиться

Ars
class Base - некий стандартный обработчик
Derived - имеет свою обработку
Test - свою
Но при этом Derived подчинен Test'у и имее свои методы отличные от Test

Автор: Vyacheslav 3.11.2003, 17:10
Цитата(Ars @ 3.11.2003, 15:24)
Билдер такое откомпилить не даст, да и зачем?

Это почему? Если Base не производный от TObject, то позволяет в соответствии со стандартом С++

Автор: Ars 3.11.2003, 17:35
Цитата
Если Base не производный от TObject, то позволяет в соответствии со стандартом С++

Прошу прощения, это Warning:
W8024 Base class 'Base' is also a base class of 'Derived'.
И, истинно, он прав. Какой смысл повторно наследовать от Base?

Код
class Base {...};
class Derived: public Base
{
   virtual DerivedMethod(){...};
};
class Test: public Derived{...};
{
   DerivedMethod(){другая начинка};
};

А в 6-м билдере дозволено множественное наследование и потомков TObject


Автор: maxim1000 3.11.2003, 18:00
упс...
немного ошибся
показалось, что это ничему не противоречит, а оно запрещено (по крайней мере пробовал в VC, BC)
там как раз выдаются ошибки
P.S.
а зачем така схема?

Автор: RAN 4.11.2003, 07:14
По идее, принципам ООП такое определение не противоречит. Вопрос "зачем это надо", по-моему, не к месту. Мало ли зачем человеку два экземляра базового класса. Но не компилиться. Говорит, что потом к этому базовому классу не добраться.

Автор: Vyacheslav 4.11.2003, 10:03
Цитата(Ars @ 3.11.2003, 17:35)
А в 6-м билдере дозволено множественное наследование и потомков TObject

Это справедливо только для наследования интерфейсов (полнсотью абстрактных классов). Для "нормальных" классов ограничения на наследование с TObject сохраняются

Автор: Ars 4.11.2003, 12:28
Цитата(RAN @ 4.11.2003, 07:14)
По идее, принципам ООП такое определение не противоречит. Вопрос "зачем это надо", по-моему, не к месту. Мало ли зачем человеку два экземляра базового класса. Но не компилиться. Говорит, что потом к этому базовому классу не добраться.

Значит, с точки зрения компиллятора это принципам ООП всетаки противоречит. Для него возникает неразрешимая неоднозначность sad.gif
Код
class TBase
{
public:
int Value;
TBase(){Value=1;};
};
class TDerived : public TBase
{
public:
TDerived(){Value=2;};
};
class TIzvrat : public TBase, public TDerived
{
public:
TIzvrat(){};
};

Код
{
TIzvrat *Izvrat=new TIzvrat();
int I=Izvrat->Value;
// Какое значение должна принять переменная I confused.gif
}

Автор: shedon 4.11.2003, 12:35
Да, по-моему, это вообще чушь, за чем два раза наследовать одно и тоже?

Автор: bel_nikita 4.11.2003, 13:20
shedon
Цитата
Да, по-моему, это вообще чушь, за чем два раза наследовать одно и тоже?


Пример:

class cA {...};

class cB: public/private cA {...};

class cD: public/private cA {...};

class cE: public/private cB, public/private cD {...};

Такое допустимо, и наследуется два раза одно и тожеsmile.gif
А вот так:

class cE: public/private cB, public/private cA {...};

не работает!
Почему?sad.gif

Автор: shedon 4.11.2003, 13:49
Цитата
Такое допустимо, и наследуется два раза одно и тоже

Такое допустимо, т.к. ты наследуешь два разных класса(cB и cD), у которых один базовый класс, такое может быть(класс cA в таком случае наследуется не явно). А вот когда ты наследуешь cB и соответственно наследуешь cA, плюс ещё явно наследуешь cA. Зачем это нужно ?

Автор: bel_nikita 4.11.2003, 16:34
shedon
Цитата
Зачем это нужно ?

И вопрос был в том, что возможно ли реализовать такую схему? А не в том зачем это нужно? В том то все и дело, что это нужно.
Пример:

есть некоторый стандартный обработчик, допустим class cA{...} - отвечающий и обрабатывающий работу таймеров. class cB: public cA {...} - имеет свои методы на события cA.
class cС: public cA {...} - имеет свои методы на события cA.
Но в тоже время class cС должен также включать в себя методы class cB.

Автор: maxim1000 4.11.2003, 17:27
но ведь класс cB включает методы класс cA, значит, при подключении cB в класс cC в нем и так будут методы обоих классов

Автор: bel_nikita 5.11.2003, 10:06
maxim1000
Цитата
но ведь класс cB включает методы класс cA, значит, при подключении cB в класс cC в нем и так будут методы обоих классов


это так, но сА иммет виртуальный метод. class cC - не может изменить метод cB. У него должен быть свой метод на сА.

Автор: Vyacheslav 5.11.2003, 17:17
Цитата(bel_nikita @ 4.11.2003, 13:20)
А вот так:

class cE: public/private cB, public/private cA {...};

не работает!
Почему?sad.gif

Ну а кто тебе мешает использовать промежуточный класс
Код

class cA {...}; //здесь есть функционал

class cB: public  cA {...}; //здесь тоже есть

class cD: public cA {}; // промежуточный класс - здесь функционал не изменен

class cE: public cB, public cD {...};


Что же касается вопроса: "Почему?sad.gif", то возможно это несоответствие стандарту. По крайней мере стандарт это не запрещает (во всяком случае я так понял 10.1 параграф 3)
Цитата

The сlass can be not specified as direct base class of the derived class more then once. [Note: class can be an indirect base class more then once and  can be direct and indirect base class . ...


"Класс не может быть специфирован как прямой базовый класс производного класса более чем одинраз. Примечание: класс может быть косвенным базовым классом более одного раза и может быть прямым и косвенным базовым классом "



Автор: Vyacheslav 5.11.2003, 17:39
Цитата(Ars @ 4.11.2003, 12:28)
Значит, с точки зрения компиллятора это принципам ООП всетаки противоречит.

Если каждый раз оглядываться на конкретную реализацию компилятора, то от стандарта ничего не останется.
Цитата(Ars)

Для него возникает неразрешимая неоднозначность  sad.gif

Интересно, откуда она возмется? По стандарту при таком наследовании создаются две копии TBase
А этот код действительно не пройдет.
Цитата(Ars)

Код
{
TIzvrat *Izvrat=new TIzvrat();
int I=Izvrat->Value;
// Какое значение должна принять переменная I confused.gif
}


Тут надо обязательно уточнять:значение Value которой копии TBase нам требуется. Поэтому нужно так
Код

TIzvrat *Izvrat=new TIzvrat();
int I=Izvrat->TBase::Value;
int J = Izvrat->TDerived::Value;

Автор: Ars 5.11.2003, 18:46
Код
int I=Izvrat->TBase::Value;

Вот-вот. Как укажешь TBase, там-то он и загнется от невозможности решить, из какой же копии брать значение smile.gif

Автор: mr.DUDA 5.11.2003, 20:35
Народ, мне вот интересно, а почему никто не вспомнил про виртуальные базовые классы ?

Код
class  X {...};
class  Y : public virtual X {...};
class  Z : public virtual X {...};
class  D : public Y, public Z  {...};


При обычном наследовании (без virtual) схема такова:
X ----> Y ----> D <---- Z <---- X

При виртуальном - схема наследования будет такова:

D<----- Y <---- X
+<----- Z <---- +


ИМХО, это именно то, что надо спросившему.

© Подбельский В.В. "Язык Си++", 1999

Автор: RAN 5.11.2003, 22:25
Не вспомнили, потому что вопрос не про виртуальное наследование.

Если говорить про виртуальное наследование, то я вот с чем сегодня столкнулся. Изучая заголовочные файлы VisiBroker'а, обнаружил такую схему наследования:

class A {
public:
virtual void f();
};

class B : virtual public A {
public:
virtual void f();
};

class C : virtual public B, virtual public A {

};

Во-первых, не понятно зачем вообще наследовать С от 2 классов. Это я объясняю тем, что разработчики так "подстраховались" smile.gif . Схема намного сложнее и запутанее, а я просто разрез привёл.
Во-вторых, при компиляции в VC выдаётся предупреждение, что функции A::f() не доступна.

Автор: Vyacheslav 6.11.2003, 10:24
Цитата(Ars @ 5.11.2003, 18:46)
Код
int I=Izvrat->TBase::Value;

Вот-вот. Как укажешь TBase, там-то он и загнется от невозможности решить, из какой же копии брать значение smile.gif

Я так понимаю, Вы уже со стандартом спорите , а не со мной. И кто интересно загнется? Компилятор? Проверить очень просто. Возьмите компилятор, который поддерживает стандарт, например VC7++ и проверьте, загнется или не загнется
А компилятор четко понимает, что

TIzvtar::TBase::Value это совсем другое, чем
TIzvrat::TDerived::TBase::Value

Автор: Ars 6.11.2003, 11:04
Цитата
Я так понимаю, Вы уже со стандартом спорите , а не со мной. И кто интересно загнется? Компилятор? Проверить очень просто. Возьмите компилятор, который поддерживает стандарт, например VC7++ и проверьте, загнется или не загнется
А компилятор четко понимает, что

TIzvtar::TBase::Value это совсем другое, чем
TIzvrat::TDerived::TBase::Value


Можно и на ты smile.gif
Я все-таки иногда проверяю то, что пишу в форум и это как раз один из таких случаев wink.gif
А результаты таковы.
Компилим вышеприведенный код под CBuilder6:
Цитата
Build
  [C++ Warning] Unit1.h(37): W8024 Base class 'TBase' is also a base class of 'TDerived'
  [C++ Error] Unit1.cpp(33): E2312 'TBase' is not an unambiguous base class of 'TIzvrat'
  [C++ Warning] Unit1.cpp(34): W8004 'Izvrat' is assigned a value that is never used

Под Visual C++ 6.0:
Цитата
Compiling...
Test.cpp
Test.cpp(20) : error C2584: 'TIzvrat' : direct base 'TBase' is inaccessible; already a base of 'TDerived'
        Test.cpp(9) : see declaration of 'TBase'

Т.е. int I=Izvrat->TBase::Value; в двух ведущих компиляторах не проходит.
Я не хочу сказать, что это все противоречит принципам ООП, а только то, что это реализовать не получится, да и не нужно, кроме случаев академического программирования

Автор: Vyacheslav 6.11.2003, 12:33
К сожалению компиляторы С++Builder 6 и Visual C++ 6.0 в данном аспекте не соответсвуют стандарту.
Кстати они далеко не ведущие. Я же указал, надо использовать Visual C++ 7.0. Что касается С++Builder 6, я уже пару дней назад отправил Borland'у bug report за номером 6367.
Код

struct A
{
  int val;
  A(){val = 1;}
};

struct B : public A
{
   B():A(){val = 2;}
};

struct C: public B, public A
{
   // C() :B(),A(){}    [C++ Error]  Unit1.cpp(17): E2312 'A' is not an unambiguous base class of 'C'
       C(){};
};

int main(int argc, char* argv[])
{

C c;
// int i =  c.A::val;   [C++ Error] Unit1.cpp(29): E2316 'A' is not a member of 'C'
// int i =  c.val;       [C++ Error] Unit1.cpp(29): E2014 Member is ambiguous: 'A::val' and 'A::val'
int j =  c.B::val;
return 0;
}



Что касается чисто академического вопроса, то практически он спокойно решается
Код

class TBase
{
public:
int Value;
TBase(){Value=1;};
};
class TBase1 : public TBase{};

class TDerived : public TBase
{
public:
TDerived(){Value=2;};
};
class TIzvrat : public TBase1, public TDerived
{
public:
TIzvrat(){};
};
//---------------------------------------------------------------------------

#pragma argsused
int main(int argc, char* argv[])
{

TIzvrat Izvrat;
int i =  Izvrat.TBase1::Value;    //i == 1
int j =  Izvrat.TDerived::Value; // j== 2
 return 0;
}

При этом фактически отличий от первого варианта нет

Автор: Ars 6.11.2003, 12:40
Ну, сдаюсь, сдаюсь. smile.gif

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)