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


Автор: Rickert 30.10.2008, 07:30
Так и не смог реализовать такую вещь: у меня есть класс, в котором должен быть список объектов, с разным кол-вом параметров.
Код
enum clsType
{
  CLS_BASE = -128,
  CLS_DEMO1,
  CLS_DEMO2
}
class baseClass
{
  public:
    baseClass() {type = CLS_BASE;};
    virtual void commonFunction() = 0;

  clsType type;
};

class demoClass : public baseClass
{
  public:
    demoClass() {i = 0; j = 0;type = CLS_DEMO1;}
    void commonFunction() {printf("demoClass");}

  int i, j;
}

class demoClass2 : public baseClass
{
  public:
    demoClass2() {i = 0; j = 0;type = CLS_DEMO2;}

  string tmpString
}

class classContainer
{
  public:
    classContainer() {}

  aList<baseClass> list;//условный класс списка
};

Теперь смысл в следующем: как мне добавлять в список объекты типа demoClass, demoClass2, чтобы потом, внутир classContainer, распозновать их по типу и работать с ними? Придумал только вместо списка делать указатель типа void.

Автор: Lazin 30.10.2008, 09:01
Код

baseClass* p = ...;
demoClass2* pp = dynamic_cast<demoClass2>(p);
if (pp)
{
    объект, на который указывает р имеет тип demoClass2
}
else
{
    объект, на который указывает р, имеет какой-то другой тип
}

в принципе, если ты точно знаешь тип объекта, можно просто привести тип и все

Автор: mes 30.10.2008, 13:36
посмотрите патерн visitor, может подойти для вашей задачи 


Автор: J0ker 30.10.2008, 18:07
боже мой
да забудьте вы уже эти касты
во-первых, как правильно заметил mes, есть паттерн visitor для сложных вещей.
для простых вполне достаточно полиморфизма:
Код

class B
{
public:
    virtual ~B() {}
    virtual void doSomething() = 0;
};

class D1: public B
{
public:
    virtual void doSomething() { ... }
};

class D2: public B
{
public:
    virtual void doSomething() { ... };
};

typedef std::list<B *> listB_t;
..........
listB_t listB;
........
for(listB_t::iterator i = listB.begin(); i != listB.end(); ++i)
    i->doSomething();

Автор: Lycifer 7.11.2008, 13:43
Базовый класс имеет такое:

enum typeClass{typeBaseClass,typeClassChildren1,typeClassChildren2}

class baseClass
{
protected:
     baseClass(){}//можно все перегрузить дабы не кто не создал объект этого класса кроме наследников

     public:
    virtual typeClass  getType(){return typeBaseClass;}
};

Реализация наследников
class ClassChildren1 : baseClass
{
    typeClass  getType(){typeClassChildren1;}
};
class ClassChildren2 : baseClass
{
    typeClass  getType(){typeClassChildren2;}
};


Используется:
void SomeFunctionClassChildren1(ClassChildren1 obj)
{
}
void SomeFunctionClassChildren2(ClassChildren2 obj)
{
}

void SomeFunction(baseClass obj)
{
     switch(obj. getType())
   {
     case typeClassChildren1:
                SomeFunctionClassChildren1((ClassChildren1)obj);//здесь можно и явное приведение
     break;
     case typeClassChildren1:
                SomeFunctionClassChildren2((ClassChildren2)obj);//здесь можно и явное приведение
    break;
     default:
     assert(false);
   }
}



}





Автор: mes 7.11.2008, 14:29
Цитата(Lycifer @  7.11.2008,  13:43 Найти цитируемый пост)
void SomeFunction(baseClass obj)
{
     switch(obj. getType())
   {
     case typeClassChildren1:

да уж... smile 

Автор: mes 7.11.2008, 14:44
вот пример, если классический полиморфизм не подходит и известны на стадии компиляции все используемые типы иерархии
Код


#include <iostream>
#include <vector>

#define VISITABLE(visitor)  public: virtual void Accept (visitor& v) { v.Visit(*this); }
class Base;
class Derived1;
class Derived2;

class IOperation
{
  public:
      virtual ~IOperation () {}
     void operator() (Base* o);
     virtual void Visit(Base&)      =0;
     virtual void Visit(Derived1&)=0;
     virtual void Visit(Derived2&) =0;
};

class Base
{
     VISITABLE(IOperation)
    public:
        virtual ~Base() {}
        void f1 () {  std::cout <<"Base::f1();" <<std::endl; }
};

class Derived1 : public Base
{
     VISITABLE(IOperation)
    public:
        void f2 (int i) {  std::cout <<"Derived1::f2(" <<i  <<");"<< std::endl;  }

};
class Derived2 : public Base
{
     VISITABLE(IOperation)
    public:
        void f3 (int a, int b) {  std::cout <<"Derived1::f3("<<a <<"," <<b <<");"<<std::endl;  }
};

void IOperation::operator() (Base* o) { o->Accept(*this); }

class TestFunc : public IOperation
{
    public:
        TestFunc (int a, int b, int c) : m_a(a), m_b(b), m_c(c) {}

     virtual void Visit(Base& o)     { o.f1();         }
     virtual void Visit(Derived1& o) { o.f2(m_c);      }
     virtual void Visit(Derived2& o) { o.f3(m_a, m_b); }

     private:
        int m_a, m_b,m_c;
};

int main ()
{
  std::vector <Base*> v;
  v.push_back(new Base);
  v.push_back(new Derived1);
  v.push_back(new Derived2);

   TestFunc  f (4,5,6);

    for_each (v.begin(), v.end(), f);

  system ("pause");
  return 0;
}



Автор: Lycifer 7.11.2008, 17:13
Цитата

вот пример, если классический полиморфизм не подходит и известны на стадии компиляции все используемые типы иерархии 
 - классический полиморфизм???  smile   это что?smile  - классические интерфеёсы мне известно такое выражение


И как же будеш узновать где какой тип ?
А не тоже самое с раздувжемся кодом ?
А производительность?(не стоит забывать что С++ язык статичиский, визитор это пример более динамического обращения, мой пример можно без проблем перевести к стратегиям что еще ускорит скорость, и к рамеру exe не будет увеличен)
 smile 


Автор: Lazin 7.11.2008, 17:21
Цитата(Lycifer @  7.11.2008,  17:13 Найти цитируемый пост)
А не тоже самое с раздувжемся кодом ?

а твой switch не раздувание кода? smile 

Автор: J0ker 7.11.2008, 19:44
Цитата(Lycifer @  7.11.2008,  13:43 Найти цитируемый пост)
 switch(obj. getType())

это, извиняюсь за мой французский, через жопу называется  smile 

Автор: mes 7.11.2008, 20:35
Цитата(Lycifer @  7.11.2008,  17:13 Найти цитируемый пост)
классический полиморфизм???  smile   это что?smile  - 

более точным будет выражение классическое примение полиморфизма, что в принципе означает стандартное применение виртуальных функций.

Цитата(Lycifer @  7.11.2008,  17:13 Найти цитируемый пост)
И как же будеш узновать где какой тип ?

Этим и хорош код, что узнавать какой тип будет компилятор, а программисту лишь надо описать реализацию действия, например
ваш участок кода с самостоятельным слежением за типами и типопреобразованиями
Цитата(Lycifer @  7.11.2008,  13:43 Найти цитируемый пост)
void SomeFunction(baseClass obj)
{
     switch(obj. getType())
   {
     case typeClassChildren1:
                SomeFunctionClassChildren1((ClassChildren1)obj);//здесь можно и явное приведение
     break;
     case typeClassChildren1:
                SomeFunctionClassChildren2((ClassChildren2)obj);//здесь можно и явное приведение
    break;
     default:
     assert(false);
}

аналогичен этому, в котором черная работа осталась за кадром:
Код

class SomeFunc : public IOperation
{
    public:
     
     virtual void Visit(Base& o)       { ..  }
     virtual void Visit(Derived1& o) { ..  }
     virtual void Visit(Derived2& o) { ..  }
};


Ну и что удобней ? 

Цитата(Lycifer @  7.11.2008,  17:13 Найти цитируемый пост)
А производительность?

Двойной вызов виртуальной функции - вот и все накладные расходы.
Цитата(Lycifer @  7.11.2008,  17:13 Найти цитируемый пост)
(не стоит забывать что С++ язык статичиский, визитор это пример более динамического обращения, мой пример можно без проблем перевести к стратегиям что еще ускорит скорость, и к рамеру exe не будет увеличен)

Вот вот - не стоит забывать, что Сpp является языком со статической типизацией. Именно на этой возможности и основан паттерн визитор и в своем исполнении не имеет ни капли динамического 
типопреобразования . Таблица переходов будет определена еще на этапе компиляции. 
Интересно с чего взяли про увелечение кода, при использовании визитора ? ) 
Визитор - это статический свитч, определеный как (в данном случае 2хмерная) таблица переходов, заполняемая и контролируемая, компилятором. 





Автор: UnrealMan 8.11.2008, 17:56
Цитата(Rickert @  30.10.2008,  08:30 Найти цитируемый пост)
Теперь смысл в следующем: как мне добавлять в список объекты типа demoClass, demoClass2, чтобы потом, внутир classContainer, распозновать их по типу и работать с ними? Придумал только вместо списка делать указатель типа void.

Юзай http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_list.html

Автор: Lycifer 11.11.2008, 11:33
Цитата

Теперь смысл в следующем: как мне добавлять в список объекты типа demoClass, demoClass2, чтобы потом, внутир classContainer, распозновать их по типу и работать с ними? Придумал только вместо списка делать указатель типа void.
 - это был вопрос, я думаю автору нужно знать и иметь доступ к получению типа, в любой момент,(typeid не будем обсуждать это довно забытое), к тамуже пример который приводит mes - способен решать немного другую проблему "когда не имеет значения тип объекта" - а вот это иногда не подходит, obj. getType() -  это обращения такого вида
 - покажи что ты умееш
 - Умееш так , тогда делай вот это .....
 - А так ты так не умееш тогда делай по другому .... 
Но в моём примере есть не достаток когда код растет(Добовляются новые классы удалять старые очень сложно особенно если это по коду разброссано, но это уже другой разговор)

Автор: mes 11.11.2008, 14:31
Цитата(Lycifer @  11.11.2008,  11:33 Найти цитируемый пост)
Но в моём примере есть не достаток когда код растет(Добовляются новые классы удалять старые очень сложно особенно если это по коду разброссано, но это уже другой разговор) 

Ага все остается на совести программиста, и если он забыл посетить один из дальних уголков, то поведение системы непредсказуемо.
Второй недостаток, что в каждой точке программы надо помнить что умеет делать каждый из объектов и как. 


Цитата(Lycifer @  11.11.2008,  11:33 Найти цитируемый пост)
- покажи что ты умееш
 - Умееш так , тогда делай вот это .....
 - А так ты так не умееш тогда делай по другому ...

А чем хуже такое ?
- а ну ка выполни это так, как умеешь. Но чтоб было хорошо!

Основная  прелесть ООП в том, что можно заставить объект самого решать поставленую задачу.
Механизм виртуальных функций, помогает в этом, позволяя осуществить позднее связывание реализации с интерфейсом и при этом соблюсти все проверки типов на стадии компиляции.

Классический полиморфизм используются для решния задач, когда кол-во выполняемых стабильно, в отличии от иерархии классов.
Т.е мы знаем интерфейс но не знаем сколько и каких объектов будет.
Двойная диспатчеризация (паттерн визитор) применяется в обратном случае, когда иерархия известна и стабильна, но интерфейс должен допускать легкое расширение функционала.

Цитата(Lycifer @  11.11.2008,  11:33 Найти цитируемый пост)
я думаю автору нужно знать и иметь доступ к получению типа, в любой момент

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

class GetTypeNameOperation : public IOperation
{
    public:
     GetTypeName  ()  { m_Res = "NullType"; }
     string Result ()     { return m_Res; }

   public:
     virtual void Visit(Base& o)       { m_Res = "Base";        }
     virtual void Visit(Derived1& o) { m_Res = "Derived1";  }
     virtual void Visit(Derived2& o) { m_Res = "Derived2";  }
   private:
       string m_Res;
      
};

string GetTypeName (Base *b)
{
      GetTypeNameOperation op; 
      op(b); return op.Result;
}

void PrintTypeName (Base *b)
{
     std::cout <<" Type name of object is " <<GetTypeName (Base *b); << endl;
}

int main()
{
  std::vector <Base*> v;
  v.push_back(new Base);
  v.push_back(new Derived1);
  v.push_back(new Derived2);

  for_each (v.begin(), v.end(), &PrintTypeName)
  
  return 0;
}


Автор: Lycifer 11.11.2008, 14:59
Цитата

А чем хуже такое ?
- а ну ка выполни это так, как умеешь. Но чтоб было хорошо!
  -

А если выполнить надо операции не имеющиго прямого связаности с классом? А тогда все просто мы просто зделаем прямую зависимоть нашего VISITOR от старонних действий!! 
1)Локализация логики  -  чего? не она негодится мы же программисты серьёзные мы все зделаем в одном VISITOR'S(набит как самосат но работает......)

2)VISITOR'S он способен выполнить действия которые были у него внутри, то и есть что бы он что-то еще выполнил лезим во все классы, а что? Нам что не хочется перелопатить еще строк так это N стока? Да не , мы же программисты и постоянная отладка и печать текста нам по приколу......

3)

Цитата

string GetTypeName (Base *b)
{
      GetTypeNameOperation op; 
      op(b); return op.Result;
}
void PrintTypeName (Base *b)
{
     std::cout <<" Type name of object is " <<GetTypeName (Base *b); << endl;
}
  вот это ООП ........


4) Динамический полиморфизм стал классическим  smile  - и какая здесь связь?(вообщен это всё классический интерфейсы) классикой полиформизма была перегрузка функций унаследованная от С то и есть что статический полиморфизм был зараждён ранее и его до сех пор используют , а динамический не все используют и он сравнительно новый(виртуальная таблица это последние что было в полиморфизме на сегоднишний день, к стати реализация виртульной таблицы считается классикой если реализовывать по средством С, в принципе это не таблица а указатель vtbl, но  это другой вопрос....)

5) 
Цитата

Двойной вызов виртуальной функции - вот и все накладные расходы.
 - это мало? по крайне мери это намного медленее чем вызов обычной функции без фычесления адресса.



Автор: mes 11.11.2008, 15:27
Цитата(Lycifer @  11.11.2008,  14:59 Найти цитируемый пост)
 - это мало? по крайне мери это намного медленее чем вызов обычной функции без фычесления адресса.

В вашем случае один Вызов виртуальной функции плюс свитч, что ставит скорости на один уровень.

Цитата(Lycifer @  11.11.2008,  14:59 Найти цитируемый пост)
4) Динамический полиморфизм стал классическим  smile  - и какая здесь связь?(

Речь идет не о типе полиморфизма, динамическом или статическом, а о способе использования.
Классический - линейный  - выбор реализации функции зависит от типа объекта
Двойная диспатчеризация - матричный - выбор основывается на типах двух объектов.
Это применимо как к статическому полиморфизму, так и и к динамическому.
Динамический же мы используем в нашем задании потому, что нам требуется хранить все наши типы в одном контейнере, и использовать элементы, зная базовый интерфейс.


Цитата(Lycifer @  11.11.2008,  14:59 Найти цитируемый пост)
 вот это ООП ........

ООП отличается от процедурного не тем, что в первом используются классы, а тем логика выполнения задания объектом локализована. 
Приведенные функции в примере являются вспомогательными. И не "покушаются"  внутренних особеностей объекта.

Автор: Torsten 11.11.2008, 15:32
Цитата(Rickert @  30.10.2008,  07:30 Найти цитируемый пост)
Теперь смысл в следующем: как мне добавлять в список объекты типа demoClass, demoClass2, чтобы потом, внутир classContainer, распозновать их по типу и работать с ними? Придумал только вместо списка делать указатель типа void.

Код
aList<baseClass*> list;//условный класс списка

Тока конечно еще лучше, как предложили юзать boost::ptr_list, т.к. он более удобен для работы с указателями и сам удаляем обьекты.

Автор: mes 11.11.2008, 16:08
Цитата(Lycifer @  11.11.2008,  14:59 Найти цитируемый пост)
А если выполнить надо операции не имеющиго прямого связаности с классом? А тогда все просто мы просто зделаем прямую зависимоть нашего VISITOR от старонних действий!! 

а кто-то говорил , что надо запихивать все в один класс ? кесарю - кесарево  smile 


Цитата(Lycifer @  11.11.2008,  14:59 Найти цитируемый пост)
1)Локализация логики  -  чего? не она негодится мы же программисты серьёзные мы все зделаем в одном VISITOR'S(набит как самосат но работает......)
 см. предыдуший абзац ) 

Цитата(Lycifer @  11.11.2008,  14:59 Найти цитируемый пост)

2)VISITOR'S он способен выполнить действия которые были у него внутри, то и есть что бы он что-то еще выполнил лезим во все классы, а что?

честно сказать мысль тут понял не до конца. Если речь о том что визитор должен реализовывать всю конкретную реализацию для каждого "посещаемого типа"объекта,
то это не совершено не так. 

 Визитор всего лишь switch. И нужен для того,
1. чтоб не было такого :
Цитата(Lycifer @  7.11.2008,  13:43 Найти цитируемый пост)
 SomeFunctionClassChildren1((ClassChildren1)obj);//здесь можно и явное приведение

2. чтоб при изменении иерархии, компилятор бы визжал бы, а не позволял бы скомпилировать неправильный код, из за того что программист не удержал в памяти все места сравнения.
3. чтоб вызовы были определены и проверены на стадии компиляции, a не оставлялась возможность ошибки рантайма :
Цитата(Lycifer @  7.11.2008,  13:43 Найти цитируемый пост)
  default:
     assert(false);

 smile 




Автор: J0ker 11.11.2008, 21:22
складывается впечатление, что Lycifer слышит звон, да не знает где он  smile 

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