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


Автор: Programister0 4.6.2007, 14:24
допустим есть шаблон класса
Код

template <class TYPE>
class s0
{
  private:
        TYPE   *v;
        DWORD   n;           

  public:
      s0(){v=new TYPE[10];};
      s0(const s0    &s)
        {
          n=s.n;
          v=new TYPE[n+1];
          memcpy(v,s.v,n);
        };
     ~s0()
        {
          delete [] v;
        }; 
};


и есть два предка от этого класса
первый:
Код

template <class TYPE>
class s0_1: public s0<TYPE>
{
  private:
        TYPE     *v1;          
  public:
      s0_1():s0<TYPE>()                //сначала вызываем конструктор для переменных s0
        {
          v1=new TYPE[10];            //затем пишем свой для s0_1
        };
      s0_1(const s0_1    &s):s0<TYPE>(s)
        {
          v1=new TYPE[n+1];
          memcpy(v1,s.v1,n);
        };
     ~s0_1()
        {
          delete [] v1;                   //здесь освобождаем только для v1 
                                                //т.к. после ~s0_1() деструктор  для ~s0()  
                                                //вызывается автоматически
        };  
};


втрой:
Код

template <class TYPE>
class s0_2: public s0<TYPE>   //практически тоже самое что и s0_1
{
  private:
        TYPE     *v2;
        DWORD   n2;                    //за исключением  вот этого     
  public:
      s0_2():s0<TYPE>()        
        {
          v2=new TYPE[10];          
        };
      s0_2(const s0_2    &s):s0<TYPE>(s)
        {
          n2=n;
          v2=new TYPE[n2+1];
          memcpy(v2,s.v2,n2);
        };
     ~s0_2()
        {
          delete [] v2; 
        };  
};


а вот и вопрос:
надо создать класс включающий в себя всё из предыдущих 3-х классов
Код

template <class TYPE>
class s1: public s0_1<TYPE>,public s0_2<TYPE> 
//нужно ли писать public s0<TYPE> или это и так понятно компилятору
{

  public:
      s1():???                 //какие конструкторы здесь 
        {                      //вызывать чтобы все работало корректно            
        };
      s1(const s1   &s)::???
        {
        };
     ~s1(){};                 //нужен ли деструктор в этом классе если 
                              // нет никаких динамич-х переменных
};

вообще возможно ли как нибудь 
попроще написать чтото подобное?




Автор: nickless 4.6.2007, 19:57
Чтобы можно было использовать аттрибуты класса в потомках (например n в s0_1), они должны быть в секции protected.

Цитата(Programister0 @  4.6.2007,  13:24 Найти цитируемый пост)
и есть два предка от этого класса

Для s0 это потомки, предки они только для s1

Цитата(Programister0 @  4.6.2007,  13:24 Найти цитируемый пост)
class s0_1: public s0<TYPE>
...
          v1=new TYPE[n+1];

Тут надо написать s0<TYPE>::n (это ИМХО изза шаблонов). В s0_2 к стати n тоже пару раз используется.

Цитата(Programister0 @  4.6.2007,  13:24 Найти цитируемый пост)
нужно ли писать public s0<TYPE> или это и так понятно компилятору

Не напишешь - будет protected

Цитата(Programister0 @  4.6.2007,  13:24 Найти цитируемый пост)
какие конструкторы здесь вызывать

s0_1<TYPE>() и s0_2<TYPE>() - т.е. всех классов, от которых наследуем.
Заметь, что в этом случае, s1 будет иметь 2 копии всех аттрибутов и методов класса s0, и нужно будет явно указывать, через какой класс (s0_1 или s0_2) ты к ним хочешь обратится.
Т.е. например s0_1<TYPE>::n и s0_2<TYPE>::n - это разные переменные.

Цитата(Programister0 @  4.6.2007,  13:24 Найти цитируемый пост)
нужен ли деструктор в этом классе

Нет.

Цитата(Programister0 @  4.6.2007,  13:24 Найти цитируемый пост)
вообще возможно ли как нибудь попроще написать чтото подобное?

Смотря что нужно, если обязательно множественное наследование и шаблоны - сомневаюсь  smile 

Автор: Rockie 4.6.2007, 20:15
Цитата

 public:
      s1():???                 //какие конструкторы здесь 
        {                          //вызывать чтобы все работало корректно    

Programister0, вроде бы imho никакие.. У базовых классов есть конструкторы по умолчанию, поэтому явно вызывать конструкторы предков нет необходимости.
Цитата

     ~s1(){};           //нужен ли деструктор в этом классе если 
                              // нет никаких динамич-х переменных

imho в С++ вообще нужны деструкторы. Если ты не объявишь его, компилятор сам его сгенерирует.


Автор: Programister0 4.6.2007, 23:20
Цитата

и есть два предка от этого класса

оговорился т.е. опечатался?

Цитата

class s0_1: public s0<TYPE>
...
          v1=new TYPE[n+1];
Тут надо написать s0<TYPE>::n (это ИМХО изза шаблонов). В s0_2 к стати n тоже пару раз используется.

пнятно 
я писал это в Builder'е он вроде понял, но возьму на заметку

Код

template <class TYPE>
class s1: public s0_1<TYPE>,public s0_2<TYPE>
[quote]
s0 Не напишешь - будет protected
[/quote]
//т.е. все функции и св-ва s0 которые копируются 2-раза будут доступны только внутри этого класса
{
  public:
      s1():s0_1<TYPE>():s0_2<TYPE>()
[quote]
s0_1<TYPE>() и s0_2<TYPE>() - т.е. всех классов, от которых наследуем.
[/quote]
        {
        };
      s1(const s1   &s):s0_1<TYPE>(s):s0_2<TYPE>(s)
        {
        };
     ~s1(){};

};

вот в этом и пробема 
размер 
s0=8,
s0_1=12 =s0+s0_1
s0_2=16 =s0+s0_2
s1=28     =s0+s0+s0_1+s0_2

как обьяснить компилятору что s0 нужна только в 1-м экземпляре в s1
если её(s0) функции не переопределяются в s0_1 и s0_2.

благодарю за разьснение;


Автор: nickless 4.6.2007, 23:35
Цитата(Programister0 @  4.6.2007,  22:20 Найти цитируемый пост)
как обьяснить компилятору что s0 нужна только в 1-м экземпляре в s1

Используй виртуальное наследование, т.е. 
Код

class s0_1: virtual public s0<TYPE>
и
class s0_2: virtual public s0<TYPE>

Читай про это например http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.9

Автор: Damarus 5.6.2007, 07:22
Цитата(Rockie @  4.6.2007,  20:15 Найти цитируемый пост)
imho в С++ вообще нужны деструкторы. Если ты не объявишь его, компилятор сам его сгенерирует.


Rockie, ты это загнул  smile 

Автор: Rockie 5.6.2007, 13:04
Damarus, хе)) Ну так оно и есть smile default destructor



Автор: Damarus 5.6.2007, 17:07
default destructor - это конечно да, только я про первую часть smile 

Автор: Rockie 5.6.2007, 17:40
Damarus, а, это мы легко  smile 



Автор: Programister0 5.6.2007, 18:19
Код

template <class TYPE>
class s0
{
  protected:
        TYPE   *v;
        DWORD   n;           
  public:
      s0(){v=new TYPE[10];};
      s0(const s0    &s)
        {
          n=s.n;
          v=new TYPE[n+1];
          memcpy(v,s.v,n);
        };
     ~s0()
        {
          delete [] v;
        }; 
};

template <class TYPE>
class s0_1: public virtual s0<TYPE>  //virtual
{
  protected:
        TYPE     *v1;          
  public:
      s0_1():s0<TYPE>()
        {
          v1=new TYPE[10];
        };
      s0_1(const s0_1    &s):s0<TYPE>(s)
        {
          v1=new TYPE[n+1];
          memcpy(v1,s.v1,n);
        };
     ~s0_1()
        {
          delete [] v1;


        };  
};

template <class TYPE>
class s0_2: public virtual  s0<TYPE>   //virtual
{
  protected:
        TYPE   *v2;
        DWORD   n2;
  public:
      s0_2():s0<TYPE>()        
        {
          v2=new TYPE[10];          
        };
      s0_2(const s0_2    &s):s0<TYPE>(s)
        {
          n2=n;
          v2=new TYPE[n2+1];
          memcpy(v2,s.v2,n2);
        };
     ~s0_2()
        {
          delete [] v2; 
        };  
};

template <class TYPE>
class s1: public s0_1<TYPE>,public s0_2<TYPE>
{
  public:
      s1():s0_1<TYPE>():s0_2<TYPE>()
        {
        };
      s1(const s1   &s):s0_1<TYPE>(s):s0_2<TYPE>(s)
        {
        };
     ~s1(){};

};

вроде так, если правильно понял но
получилось ещё хуже 

size(s0  )=8
size(s0_1)=16  //вместо 12 
size(s0_2)=20  //вместо 16
size(s1  )=28   //вместо 20 как хотелось бы

вот и второй вопрос: 
куда уходят по 4 байта в классах s0_1 и s0_2 
что ещё дает это переопределие "virtual"

в том примере из ссылки такая-же ситуация:
Код

 class Base {
 protected:
   int data_;
 };
 
 class Der1 : public virtual Base {
 public:

 };
 
 class Der2 : public virtual Base {
 public:
 };
 
 class Join : public Der1, public Der2 {
 public:
   void method()
   {
      data_ = 1; 
   }
 };

size(Base)=4
size(Der1)=8  //откуда ещё 4-е байта?
size(Der2)=8
size(Join)=12 

во втором примере без данных ещё хуже:
size(Base)=4
size(Der1)=12
size(Der2)=12
size(Join)=20


Автор: nickless 5.6.2007, 20:30
Цитата(Programister0 @  5.6.2007,  17:19 Найти цитируемый пост)
куда уходят по 4 байта в классах s0_1 и s0_2

В классах с виртуальными функциями или в наследованых классах есть еще указатель на таблицу виртуальных методов (VMT), (это зависит от реализации и может быть по другому в разных компиляторах, но в большинстве это так).
Т.е.
size(s0<int>  )=8 // TYPE* + DWORD = 4 + 4
size(s0_1<int>)=16  //VMT* + TYPE* + base = 4 + 4 + 8
size(s0_2<int>)=20  //VMT* + TYPE* + DWORD + base = 4 + 4 + 4 + 8
size(s1<int>  )=28   //s0_1::VMT* + s01:(TYPE*) + s0_2::VMT* + s0_2:(TYPE* + DWORD) + base = 4 + 4 + 4 + 8 + 8
в общем всё правильно smile 

Цитата(Programister0 @  5.6.2007,  17:19 Найти цитируемый пост)
что ещё дает это переопределие "virtual"

Почитай дальше по ссылке, там есть примеры, например когда класс использует виртуальный метод дочернего класса, итд.

А вообще множественное наследование нужно использовать осторожно, только в особых случаях и если знаешь что делаешь smile 

Автор: archimed7592 6.6.2007, 00:27
Цитата(Programister0 @  5.6.2007,  18:19 Найти цитируемый пост)
 s1():s0_1<TYPE>():s0_2<TYPE>()
если не ошибаюсь, писать нужно так:
Код
s1():s0_1<TYPE>(), s0_2<TYPE>()
а, раз у тебя s0 в единственном экземпляре(из-за виртуального наследования), то вот так:
Код
s1():s0<TYPE>(), s0_1<TYPE>(), s0_2<TYPE>()
иначе можешь получить не совсем то, что хочешь... например, вот здесь:
Цитата(Programister0 @  5.6.2007,  18:19 Найти цитируемый пост)
s1(const s1   &s):s0_1<TYPE>(s):s0_2<TYPE>(s)
ты получишь то же самое, что если написал бы
Код
s1(const s1   &s):s0<TYPE>(), s0_1<TYPE>(s), s0_2<TYPE>(s)
но, в действительности ты же хочешь получить вот это:
Код
s1(const s1   &s):s0<TYPE>(s), s0_1<TYPE>(s), s0_2<TYPE>(s)




Цитата(nickless @  5.6.2007,  20:30 Найти цитируемый пост)
А вообще множественное наследование нужно использовать осторожно, только в особых случаях и если знаешь что делаешь smile  
 smile  smile  smile 

Автор: Programister0 6.6.2007, 12:00
большое спасибо вам о великие программеры: archimed7592, nickless, Rockie,  Damarus;
я вроде догнался с наслеованием и virtual'ами, - буду использовать только в крайних случаях, 
 но в своих классах: никогда - эт. точно;


И ещё что это у вас тут за система 
Цитата

Репутация: * [ + | — ]

мне типа надо нажать эти  + | —  - если понравились/помогли ответы, к чему все это.

ещё раз спасибо всем, вы мне очень помогли.

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