Форум программистов, компьютерный форум CyberForum.ru

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 35, средняя оценка - 4.71
Sanek911
4 / 4 / 0
Регистрация: 04.12.2009
Сообщений: 42
#1

Как узнать, какого типа объект находится по указателю - C++

26.03.2012, 23:31. Просмотров 5364. Ответов 22
Метки нет (Все метки)

Есть массив указателей базового класса, нужно узнать какие типы этих объектов.
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
class Shape{
public:
  ~Shape(){}
  virtual void Draw() = 0;
};
class Triangle : public Shape{
  virtual void Draw(){}
};
class Circle : public Shape{
  virtual void Draw(){}
};
class Square : public Shape{
  virtual void Draw(){}
};
 
void main()
{
  std::vector<Shape *> vec;
  for(int i = 0; i < 10; i++){
    if ((i % 3) == 0)
      vec.push_back(new Triangle());
    if ((i % 3) == 1)
      vec.push_back(new Circle()); 
    if ((i % 3) == 2)
      vec.push_back(new Square());
  }
//предложите код который поможет узнать типы объектов в векторе
}
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
26.03.2012, 23:31     Как узнать, какого типа объект находится по указателю
Посмотрите здесь:
C++ Как удалить объект по указателю на базовый класс?
C++ вызов конструктора, по указателю на объект
Узнать значение по указателю C++
Как узнать, находится ли std::cout в hex-режиме? C++
интерфейс, в методе которого создается объект типа IDictionary и возвращается ссылка на этот объект C++
C++ Как инициализировать объект типа std::deque<int>?
C++ Не удаётся создать безымянный объект типа vector моего типа
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Завернин
1 / 1 / 0
Регистрация: 22.12.2013
Сообщений: 27
21.02.2015, 00:39     Как узнать, какого типа объект находится по указателю #16
Цитата Сообщение от Croessmah Посмотреть сообщение
да у Александресску тоже нормально расписано. Можно сначала к его труду отослать
Цитата Сообщение от Voivoid Посмотреть сообщение
довольно приятная реализация мультиметодов на плюсах: https://github.com/jll63/yomm11
Спасибо. Просвещусь на досуге. Как я понимаю, в обоих случаях реализация осуществляется с помощью шаблонов.
Возможно они и лучше по многим аспектам, я пока эти подходы не изучил.
Я ищу подход не только для плюсов, но и для C#. Там это скорее всего будет нереализуемо.
hoggy
6440 / 2658 / 460
Регистрация: 15.11.2014
Сообщений: 5,849
Завершенные тесты: 1
21.02.2015, 01:03     Как узнать, какого типа объект находится по указателю #17
Цитата Сообщение от Завернин Посмотреть сообщение
Лучше рассмотреть на конкретной задаче:
Есть 2 контура Contour, состоящих из последовательно соединенных линий Line: Element и дуг Arc: Element.
Нам нужно найти точки пересечения этих контуров.
Пускай для простоты будет не линия, а отрезок.

Нужно отталкиваться от "способа обобщения".

Что у нас есть?
Контур, который может состоять из линий, дуг, и может быть ещё чего нибудь придумаем.

Что общего у отрезков и дуг?

Если мы можем выделить общее у этих двух элементов, например:
"отрезок - это примитив состоящий из 2х точек"
"дуга - это примитив состоящий из И отрезков"

Пойдем дальше в нашем обобщении:
отрезок - это контейнер для двух точек

Но если так,
то совокупность отрезков можно представить в виде списка точек.

А значит дуга - это контейнер из И точек.
И по аналогии, контур - это так же контейнер из N точек.

Итого: вся архитектура сводится всего к двум элементам: точка, и контейнер точек.

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

C++
1
2
3
4
5
6
7
8
//получили дугу из N точек
auto arc = Figure::create<Arc>(param1);
 
//получили отрезок
auto line = Figure::create<Line>(param2);
 
//получили замкнутую фигуру из одной дуги и одного отрезка
auto fugure = (arc + line).close();
Очевидно, что при таком обобщении достаточно одного единственного алгоритма
поиска пересечений для фигур состоящих из И точек.


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

---------------------------------------------------------------------------------------------

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

В этом случае становится очевидным,
что сделать универсальный алгоритм пересечения не получится.

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

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

И с этим мы бессильны, что либо поделать.
Мы можем лишь облегчить сопровождение такого подхода.

Например, мы можем построить решение,
которое частично может поспособствовать универсальности решения.

Допустим, если линия не умеет пересекаться с окружностью
(программисты ещё не успели доделать этот алгоритм,
но уже сейчас нужно показать промежуточные результаты работы),
то линия может попросить окружность "адаптивно деградироваться",
и в итоге отработает универсальный алгоритм.

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

И этот фактор очень важен, поскольку избавляет нас от использования медленного динамик_каст,
медленных свитч-кейсов, медленных ифов,
упрощает расширение системы,
и в конечном счете ошибки времени компиляции - это всегда лучше, чем ошибки рантайма.
Ничайно позабыть что-то уже не получится.

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

Ниже представленный код - пример-иллюстрация одного из возможных решений:

http://rextester.com/JAIUXZ12714

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <string>
#include <typeinfo>
#include <iostream>
using namespace std;
 
struct Circle;
struct Line;
struct Arc;
 
struct Element
{
    virtual void Intersect(Element& Object) = 0;
 
    virtual void Collision(Element&) = 0;
    
    //--- элементы не обязаны уметь обрабатывать данный тип фигуры
    // если они не определили алгоритм обработки - 
    // будет задействован универсальный алгоритм с применением адаптивной деградации
    virtual void Collision(Circle& el);
 
    virtual void Collision(Line&) = 0;
    virtual void Collision(Arc&) = 0;
 
    static void Collision(Line&, Line&)
        { cout << "intersection: line vs line\n";     }
    static void Collision(Line&, Arc&)
        {  cout << "intersection: line vs arc\n";     }
    static void Collision(Arc&, Arc&)
        { cout << "intersection: arc vs arc\n";       }
    static void Collision(Circle&, Circle&)
        { cout << "intersection: circle vs circle\n"; }
 
    template<class T>
    static void Collision(Circle&, T&)
        { cout << "circle vs figure with adaptive degradation...\n"; }
 
    
 
    
};
 
struct Line : Element
{
    //--- если срабатывает метод, принимающий базовый элемент
    // значит данная фигура не знает уникального алгоритма обработки
    // фактического типа этого элемента
    // в этом случае задействуется универсальный алгоритм 
    // с использованием адаптивной деградации
    virtual void Collision(Element&)
        { cout << "line vs figure with adaptive degradation...\n";  }
 
    virtual void Collision(Line& l)
        { Element::Collision(*this, l); }
    virtual void Collision(Arc& a)
        { Element::Collision(*this, a); }
    virtual void Intersect(Element& object)
        { object.Collision(*this); }
};
 
struct Arc : Element
{
    virtual void Collision(Element&)
        { cout << "arc vs figure with adaptive degradation...\n"; }
 
    virtual void Collision(Line& l)
        { Element::Collision(l, *this); }
    virtual void Collision(Arc& a)
        { Element::Collision(*this, a); }
    virtual void Intersect(Element& object)
        { object.Collision(*this); }
};
 
struct Circle : Element
{
    virtual void Collision(Element&)
        { cout << "circle vs figure with adaptive degradation...\n"; }
 
    virtual void Collision(Line& l)
        { Element::Collision(*this, l); }
    virtual void Collision(Arc& a)
        { Element::Collision(*this, a); }
    virtual void Collision(Circle& c)
        { Element::Collision(*this, c); }
    virtual void Intersect(Element& object)
        { object.Collision(*this); }
};
 
 
void Element::Collision(Circle& el)
{
    Collision(static_cast<Element&>(el));
}
 
int main()
{
    Line   line;
    Arc    arc;
    Circle cir;
 
    line.Intersect(line);
    line.Intersect(arc);
    arc.Intersect(arc);
    arc.Intersect(line);
 
    cout << "--------------------------\n";
 
    cir.Intersect(line);
    cir.Intersect(arc);
    cir.Intersect(cir);
}
Voivoid
674 / 277 / 12
Регистрация: 31.03.2013
Сообщений: 1,339
21.02.2015, 01:26     Как узнать, какого типа объект находится по указателю #18
Ну как вариант можно не городить велосипеды и заюзать boost::variant

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
41
42
43
44
45
46
#include <boost/variant.hpp>
#include <stdexcept>
 
struct line {
 
};
 
struct arc {
 
};
 
struct intersects_algo : boost::static_visitor<bool> {
    bool operator()( const line& l1, const line& l2 ) const {
        throw std::runtime_error( "not implemented" );
    }
 
    bool operator()( const arc& a1, const arc& a2 ) const {
        throw std::runtime_error( "not implemented" );
    }
 
    bool operator()( const line& l, const arc& a ) const {
        throw std::runtime_error( "not implemented" );
    }
 
    bool operator()( const arc& a, const line& l ) const {
        return this->operator()( l, a );
    }
};
 
using element = boost::variant<line, arc>;
 
bool intersects( const element& e1, const element& e2 ) {
    return boost::apply_visitor(intersects_algo(), e1, e2 );
}
 
 
 
int main() {
 
    element e1 = line{};
    element e2 = arc{};
 
    bool result = intersects( e1, e2 );
 
    return 0;
}
В качестве бонуса - compile time чек наличия обработчиков. Т.е. если добавить в variant новый тип, но при этом не обновать visitor - будет ошибка компиляции
Завернин
1 / 1 / 0
Регистрация: 22.12.2013
Сообщений: 27
21.02.2015, 15:19     Как узнать, какого типа объект находится по указателю #19
Спасибо hoggy, мне очень понравилась такая реализация. Волшебный указатель this при вызове виртуального метода Intersect сам уже посылает нужный тип в функцию, действительно очень удобно, и работает также и на шарпе. Можно назвать это автоматическим приведением типа объекта посредством виртуальной функции. Небольшой минус в интерфейсе - нужно заводить две публичные функции для одного действия.
А вообще такой подход наверное применим во многих случаях в моих задачах.


Цитата Сообщение от Voivoid Посмотреть сообщение
Ну как вариант можно не городить велосипеды и заюзать boost::variant
Может и можно. Не пользовался бустом. Не рассмотрели важный момент, что нам еще важна последовательность элементов, и они должны храниться в коллекции. Полагаю, вряд ли можно сделать коллекцию из элементов variant<line, arc?
hoggy
6440 / 2658 / 460
Регистрация: 15.11.2014
Сообщений: 5,849
Завершенные тесты: 1
21.02.2015, 15:57     Как узнать, какого типа объект находится по указателю #20
Цитата Сообщение от Завернин Посмотреть сообщение
Полагаю, вряд ли можно сделать коллекцию из элементов variant<line, arc?
Напрасно.

Добавлено через 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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//участники 
struct Line: Element {...};
struct Arc: Element {...};
struct Circle: Element {...};
 
...
 
bool Collision(int, Arc&);      //<--- мультиметоду не важны наследственные связи
bool Collision(Some, Arc&);  // участники вообще не обязаны быть полиморфами
 
bool Collision(Line&, Arc&);
 
bool Collision(Arc&, Element&);  //<--- версия с базовым классом будет выбрана автоматически, 
//если нет обработчика, способного принять вторым аргументом фактический тип
 
bool CollisionEx(Element&, Element&); //<--- Можно использовать любые имена, а не только перегрузки
//версия, которая принимает оба аргумента в виде базовых классов
//выбирается, если не было обнаружено ни одного подходящего обработчика, 
//принимающего фактические типы
 
...
 
 
//где то в бизнес-коде функция, которая получает на вход базовые интерфейсы
//и ей нужно выполнить "пересечение"
 
boo Work(Element& one, Element& two )
{
     // здесь мы уже не владеем информацией о фактических типах элементов
     // но нам очень нужно выполнить пересечение
     // для этого мы создаем мультиметод
 
     // утилита make умеет создавать мультиметоды
     auto mmethod = tools::make<multimethod>(
 
         //от пользователя требуется просто перечислить функции-обработчики
 
         // утилита make помогает разрулить коллизии перегрузок
         tools::make<int, Arc&>(Collision),   
         tools::make<Some, Arc&>(Collision),
         tools::make<Line&, Arc&>(Collision),
         tools::make<Arc&, Element&>(Collision),
         CollisionEx //<--- уникальное имя можно закинуть без всяких утилит
     );
 
     return mmetod(one,two); //<--- механизм времени компиляции распарсил аргументы
     // всех функций-обработчиков, которыми его накормили при создании
 
     //он знает какими теоретически могут быть аргументы
     //времени компиляции он проверяет входящие аргументы на предмет наследственных связей
 
    //фактически там, внутри его на шаблонах развернулась невидимая архитектура, 
    //очень сильно похожая на ту, что я приводил в предыдущем своем примере
 
    //вот только умный механизм сделал это самостоятельно, 
    //автоматически, без участия человека
 
    //от человека нужно только написать сами функции-обработчики
    //и указать их список мультиметоду.
    //все остальное он сделает сам.
 
}

зы: это лишь концепт-дизайн. Я ничего подобного ещё не делал.
Однако, мои знания шаблонов с++ дают мне основание полагать,
что подобный дизайн реален. Хотя работа не на один вечер.
VIKT0R
21.02.2015, 16:41
  #21

Не по теме:

Нет такого слова "идеома", правильно идиома.

Voivoid
674 / 277 / 12
Регистрация: 31.03.2013
Сообщений: 1,339
21.02.2015, 17:18     Как узнать, какого типа объект находится по указателю #22
Цитата Сообщение от Завернин Посмотреть сообщение
Полагаю, вряд ли можно сделать коллекцию из элементов variant<line, arc?
А зря. Вполне себе можно сделать че-нить типа такого:

C++
1
2
3
4
std::vector<boost::variant<Arc, Line>> elements;
elements.push_back( Arc{} );
elements.push_back( Line{} );
intersects( elements[0], elements[1] );
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
24.02.2015, 10:52     Как узнать, какого типа объект находится по указателю
Еще ссылки по теме:
C++ Как привести объект пользовательского типа к заданному встроенному типу?
Какого типа std::endl ? Как сделать так, чтобы функция могла принимать endl в качестве аргумента? C++
Как передавать значения строкового типа в конструктор через объект класса? C++
Приведение одного парметра-типа к другому разыменованному парметру-типу (указателю) в шаблонах C++
C++ объясните пожалуйста: tz какого типа

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

Или воспользуйтесь поиском по форуму:
Завернин
1 / 1 / 0
Регистрация: 22.12.2013
Сообщений: 27
24.02.2015, 10:52     Как узнать, какого типа объект находится по указателю #23
Цитата Сообщение от hoggy Посмотреть сообщение
Однако, мои знания шаблонов с++ дают мне основание полагать,
что подобный дизайн реален. Хотя работа не на один вечер.
Любопытный способ.
Резюме к теме:
Лично я сделал для себя вывод, что двойная диспетчеризация более правильна с точки зрения программирования и быстродействия, чем динамик касты (Хотя для простых случаев быстродействие еще стоит протестить).
Также такой подход никак не влияет на необходимость иметь или не иметь абстрактные коллекции, скорее только поощряет их применение. А если столкнусь с примером, в котором однозначно необходимы динамик касты, то выложу его сюда.
Yandex
Объявления
24.02.2015, 10:52     Как узнать, какого типа объект находится по указателю
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru