Форум программистов, компьютерный форум, киберфорум
ООП и паттерны
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 5.00/34: Рейтинг темы: голосов - 34, средняя оценка - 5.00
1980 / 836 / 115
Регистрация: 01.10.2012
Сообщений: 5,210
Записей в блоге: 2

Учебный пример ООП

13.09.2016, 11:26. Показов 9278. Ответов 123
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день

Знакомство ООП часто начинается с этого примера. Ну не "строчка в строчку" (это я взял первый попавшийся в гугле), но те же классы и методы.

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
// Базовый класс Фигура
// для него заданы методы, реализация которых
// отложена для выведенных классов
 
class Shape
{
public:
// Внешняя часть класса
    virtual bool Draw() = 0; // Перерисовать фигуру
    virtual bool Move(double _x, double _y) = 0; // Сдвинуть фигуру
    virtual bool Zoom(double scale) = 0; // Масштабировать
 
protected:
    // Защищенная часть класса, доступная только
    // выведенным из него классам
 
    double coordX; // Атрибут - координата X
    double coordY; // Атрибут - координата Y
};
 
// Класс Круг выведенный из класса Фигура
class Circle : public Shape
{
public:
    virtual bool Draw() {...}; // Реализация перерисовки
    virtual bool Move(double _x, double _y) {...}; // Реализация сдвига
    virtual bool Zoom(double scale) {...}; // Реализация операции масштабирования
 
private:
    // Внутренняя часть доступная только самому классу
    double radius; // Атрибут - длина радиуса
};
 
// Класс Квадрат выведенный из класса Фигура
class Square : public Shape
{
 
public:
    virtual bool Draw() {...}; // Реализация перерисовки
    virtual bool Move(double _x, double _y) {...}; // Реализация сдвига
    virtual bool Zoom(double scale) {...}; // Реализация операции масштабирования
 
private:
    // Внутренняя часть доступная только самому классу
    double side; // Атрибут - длина стороны квадрата
};
После первого знакомства прошло несколько лет (а для кого и много лет), теперь Вы обладаете гораздо большим опытом. Как изменилось Ваше отношение к этому примеру? Что в нем наивно (или просто плохо), а что остается верным и правильным?

С уважением
Игорь
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
13.09.2016, 11:26
Ответы с готовыми решениями:

Неизвестные переменные в сценарии(учебный пример)
Учебный сценарий l> <head> <title>Котировка акций от NASDAQ</title> </head> <body> <?php // Выбор обозначения...

Учебный пример реализации веб приложения или сайта
Нужен учебный(полноценно-рабочий) пример реализации веб приложения или сайта. Родные Erlang и OTP, а не Elixir.

Не работает учебный пример с БД: "произошла ошибка, связанная с сетью или с определенным экземпляром"
Начал писать учебный пример с msdn http://msdn.microsoft.com/ru-ru/library/bb386940.aspx написал тренировочный код: using System; ...

123
 Аватар для Fulcrum_013
2083 / 1576 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
16.09.2016, 15:50
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Shamil1 Посмотреть сообщение
Резюме:
Большинство шаблонов, которые мы привыкли реализовывать с использованием классов, прекрасно реализуются и без них.
Не надо путать причину и следствие. Не классы привнесли какие то шаблоны, а классы появились для упрощения реализации шаблонов которые использовались во всю в процедурном программировании.

Добавлено через 2 минуты
Цитата Сообщение от Shamil1 Посмотреть сообщение
Правда, непонятно, как вести иерархию этих сериализаторов - по типу сериализуемых объектов или по типу потоков.
При наличии RTTI вопрос с типами сериализируемых объектов снимается. При этом для сериализации/десериализации полиморфных данных вопрос с RTTI прийдется решать в любом случае и в любом языке.

Добавлено через 8 минут
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
class TRenderContext{
public:
 ....
  virtual void OutText(char *Text)=0;
};
class TShape{
public:
 ....
 virtual void Draw(TRenderContext *Context)=0;
};
class TCircle:public TShape{
public:
 ....
 virtual void Draw(TRenderContext *Context){Context->OutText ("Circle");}     
};
class TRectangle:public TShape{
public:
 ....
 virtual void Draw(TRenderContext *Context){Context->OutText ("Rectangle");}
       
};
class TScreenRender:public TRenderContext{
public:
    virtual void OutText(char *Text){cout<<Text<< " on screen"<<endl;}
}
class TLaserCutterRender:public TRenderContext{
public:
    virtual void OutText(char *Text){cout<<Text<<" on laser cutter"<<endl;}
}
Добавлено через 1 минуту
Цитата Сообщение от Shamil1 Посмотреть сообщение
Я обожаю эту технику. Написал по привычке. В данном случае суть была в создании разных классов WinFormCircleDrawer и WinFormSquareDrawer для Circle и Square (не важно, используется ли при этом template/generic).
Шаблоны это очень удобно там где это удобно. Только компайл-тайм полиморфизм в принципе не способен заменить рантайм полиморфизм. А вот дополняют друг друга они здорово.
0
Модератор
Эксперт функциональных языков программирования
3141 / 2289 / 469
Регистрация: 26.03.2015
Сообщений: 8,920
16.09.2016, 16:06
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
class TRenderContext
У Вас всю работу по формированию вывода выполняет метод OutText класса TRenderContext. Чем это отличается от моего "Вариант 1"? Код Вы не приводите, но видимо, внутри него такой же switch.

з.ы. Добавление ещё одного уровня абстракции - TScreenRender - к теме не относится, так как она выполняет другую задачу.
0
 Аватар для Fulcrum_013
2083 / 1576 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
16.09.2016, 16:15
Цитата Сообщение от Shamil1 Посмотреть сообщение
Код Вы не приводите, но видимо, внутри него такой же switch.
з.ы.
Никаких свичей.
C++
1
2
3
4
5
6
7
8
9
int main(){
     TRenderContext *Screen=new TScreenRender, *Cutter=new TLaserCutterRender;
     TShape* Shapes[10];
      for (int i=0;i<10;i++) Shapes[i]=i%1?new TCircle(..creation params...):new TRectanle(...creation params...); 
/*при создании инстансов их тип нужно знать в любом случае*/
     for (int i=0;i<10;i++) Shapes[i]->Draw(Screen);
     for (int i=0;i<10;i++) Shapes[i]->Draw(Cutter);
     /* а вот при использовании достаточно базового абстрактного типа*/
}
Добавление ещё одного уровня абстракции - TScreenRender - к теме не относится, так как он выполняет другую задачу.
Ну вы же говорили про отрисовку на разных девайсах не меняя код шейпов при смене девайса? Вуаля.
0
1980 / 836 / 115
Регистрация: 01.10.2012
Сообщений: 5,210
Записей в блоге: 2
16.09.2016, 17:01  [ТС]
C++
1
virtual void OutText(char *Text)=0;
По повоlу криворукости: отсутствие мрдификатора(ов) const говорит о многом (и не в Вашу пользу)

C++
1
virtual void Draw(TRenderContext *Context){Context->OutText ("Circle");}
Так легко отделаться переливанием (делегированием) не удастся. Каким шрифтом/размером выводите? Какова толщина линии? Координаты (нет, "просто масштаб" не катит) И.т.д. Все это у разных дивайсов разное, полагать что OutText "просто так" даст нужный рез-т, мягко говоря, наивно

При сериализации это еще рельефнее. Сериализуем
а) в бинарник (можно записать строку "Circlrе", а можно просто число (id)
б) в текст - а вот здесь надо записать строку "Name = Circle"

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

И вообще, ну чего так много написано? Ведь по существу хотели
C++
1
void Draw( const Circle & circle, TheDevice & device );
Ну так надо было так и писать, а не выделываться с виртуалами и мнимой общностью (которая при первом скачке развалится )
0
Модератор
Эксперт функциональных языков программирования
3141 / 2289 / 469
Регистрация: 26.03.2015
Сообщений: 8,920
16.09.2016, 17:06
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Никаких свичей.
Посмотрел более внимательно Ваш код. На самом деле у Вас "Вариант 0" - из стартового сообщения. Класс Circle знает, как себя рисовать (это знание выражается в виде литерала "Circle" в коде Draw).
Так что никаких проблем из сообщения #4 Ваш код не решает.
0
 Аватар для Fulcrum_013
2083 / 1576 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
16.09.2016, 17:16
Цитата Сообщение от Shamil1 Посмотреть сообщение
Класс Circle знает, как себя рисовать (это знание выражается в виде литерала "Circle" в коде Draw).
Он знает из каких суб-примитивов себя рисовать. А как рисовать каждый из этих суб-примитивов на конкретном устройстве вывода ему сугубо фиолетово. Это зона оветственности TRenderContext. Любой другой подход к вопросу ведет к комбинаторному взрыву.
0
Модератор
Эксперт функциональных языков программирования
3141 / 2289 / 469
Регистрация: 26.03.2015
Сообщений: 8,920
16.09.2016, 17:22
Цитата Сообщение от Igor3D Посмотреть сообщение
void Draw( const Circle & circle, TheDevice & device );
Так в Лиспе сделано. Там множественная диспетчеризация, поэтому можно при желании для каждой пары написать свой метод с тем же именем.
А у Fulcrum_013 каждый рендер должен знать про все фигуры (добавил одну - и меняй все рендеры), либо каждая фигура должна знать про все рендеры.

Добавлено через 4 минуты
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Он знает из каких суб-примитивов себя рисовать. А как рисовать каждый из этих суб-примитивов на конкретном устройстве вывода ему сугубо фиолетово.
В общем случае ответственность нельзя так поделить. Предлагаю всё-таки абстрагироваться от конкретной задачи рисования фигур.

Напишите код, который будет выводить 1, 2, 3 или 4 в зависимости от комбинации фигура-рендер.
0
 Аватар для Fulcrum_013
2083 / 1576 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
16.09.2016, 17:23
Цитата Сообщение от Shamil1 Посмотреть сообщение
А у Fulcrum_013 каждый рендер должен знать про все фигуры (добавил одну - и меняй все рендеры), либо каждая фигура должна знать про все рендеры.
Рендеры знают про набор суб-примимтивов из которых состоят шейпы. Шейпы знают как себя конструировать/модифицировать из этого набора. При этом набор примитивов обычно не просто фиксирован а резко ограничен. К примеру промышленные САПРы обычно пользуют для 3D 4 типа субпримитивов (плоскость, несколько частных случаев квадрики тор и кусочно-сплайновая поверхность). В принципе для отрисовки хватает только кусочно-сплайновой поверхности, остальные типы для удобства манипуляций, а при отрисовке конструируются как набор кусочно-сплайновых поверхностей. А все остальные сложные формы конструируются уже как взаимосвязанные наборы этих четырех типов примитивов. Вот это и есть толковый подход к анализу предметной области и архитектуре иерархий. А любой другой подход в результате даст комбинаторный взрыв при любой парадигме.
0
510 / 272 / 60
Регистрация: 14.12.2010
Сообщений: 548
16.09.2016, 17:28
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Только компайл-тайм полиморфизм в принципе не способен заменить рантайм полиморфизм.
Статический и динамический полиморфизм эквивалентны.
Разница в том, что динамический полиморфизм вводит бесконечность указателем,
а статическому полиморфизму нужна бесконечная память для эквивалентности.
Поэтому приходится пользоваться чаще динамическим полиморфизмом, т.к. память ограничена.

Вот пример по теме:
Кликните здесь для просмотра всего текста
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
#include <iostream>
#include <memory>
#include <assert.h>
#include <string>
#include <tuple>
 
//https://www.cyberforum.ru/oop/thread1806816.html
 
#define DRAW_FUNC(_obj, _text,_func) std::cout << (void*)&(_obj) << ":" << (_text) << "::" << (_func) << std::endl
 
namespace universal {
    class Config
    {
    public:
        typedef double Value;
    };
 
    class Point {
    public:
        Config::Value coordX; // Атрибут - координата X
        Config::Value coordY; // Атрибут - координата Y
 
        Point() :coordX(0.), coordY(0.) {}
        Point(Config::Value _coordX, Config::Value _coordY) :
            coordX(_coordX),
            coordY(_coordY)
        {}
 
        Point(const Point& _c) = default;
        Point(Point&& _c) = default;
        Point& operator=(const Point& _c) = default;
        Point& operator=(Point&& _c) = default;
    };
 
    class Point3D :public Point {
    public:
        Config::Value coordZ; // Атрибут - координата Z
 
        Point3D() :coordZ(0.) {}
        Point3D(Config::Value _coordX, Config::Value _coordY, Config::Value _coordZ) :
            Point(_coordX, _coordY),
            coordZ(_coordZ)
        {}
 
        Point3D(const Point& _c) {
            Point::operator=(_c);
            coordZ = 0.;
        }
 
        Point3D(const Point3D& _c) = default;
        Point3D(Point3D&& _c) = default;
        Point3D& operator=(const Point3D& _c) = default;
        Point3D& operator=(Point3D&& _c) = default;
    };
 
    class Object {
    public:
        virtual ~Object() {}
    };
}
 
namespace models
{
    template<typename Config, typename FaceShape>
    class Shape : public Config::template ShapeParent<FaceShape>
    {
    public:
        typename Config::Point& getCoord() { return coord; }
        const typename Config::Point& getCoord()const { return coord; }
 
        typename Config::ShapeType getShapeType()const { return type; }
 
    protected:
        // Защищенная часть класса, доступная только
        // выведенным из него классам
        template<typename FaceShape>
        Shape(typename Config::ShapeType /*_type*/, FaceShape& _face) :
            Config::template ShapeParent<FaceShape>(_face)
        {
        }
 
        typename Config::Point      coord;  // Атрибут - координаты
    };
 
    // Класс Круг выведенный из класса Фигура
    template<typename Config>
    class Circle : public Config::template BaseShape<Config, Circle<Config>>
    {
    public:
        typename Config::Value getRadius()const { return radius; }
        void setRadius(typename Config::Value _radius) { radius = _radius; }
 
        Circle():
            Config::template BaseShape<Config, Circle<Config>>(typename Config::ShapeType::Circle, *this)
        {}
    private:
        // Внутренняя часть доступная только самому классу
        typename Config::Value radius; // Атрибут - длина радиуса
    };
 
    // Класс Квадрат выведенный из класса Фигура
    template<typename Config>
    class Square : public Config::template BaseShape<Config, Square<Config>>
    {
    public:
        typename Config::Value getSide()const { return side; }
        void setSide(typename Config::Value _side) { side = _side; }
 
        Square():
            Config::template BaseShape<Config, Square<Config>>(typename Config::ShapeType::Square, *this)
        {}
    private:
        // Внутренняя часть доступная только самому классу
        typename Config::Value side; // Атрибут - длина стороны квадрата
    };
 
    class Config2D
    {
    public:
        typedef universal::Config::Value Value;
        typedef universal::Point Point;
        typedef universal::Object Object;
 
        template<typename FaceShape>
        using ShapeParent = universal::Object;
 
        template<typename Config, typename FaceShape>
        using BaseShape = models::Shape<Config, FaceShape>;
 
        enum class ShapeType {
            Circle,
            Square
        };
    };
}
 
namespace views_static
{
    template<typename FaceShape>
    class ShapeDrawAspect {
    protected:
        FaceShape& face;
    
    protected:
        ShapeDrawAspect(FaceShape& _face):face(_face){}
 
        ShapeDrawAspect& operator=(const ShapeDrawAspect& _c) {}
 
    public:
        template<typename Renderer>
        void draw(Renderer& _renderer)
        {
            DRAW_FUNC(*this, "ShapeDrawAspect", std::string("draw(") + typeid(_renderer).name() + "&)");
            _renderer.render(face);
        } // Перерисовать фигуру
    };
 
    template<typename FaceShape>
    class ShapeAllAspects: public ShapeDrawAspect<FaceShape> {
    protected:
        ShapeAllAspects(FaceShape& _shape):
            ShapeDrawAspect<FaceShape>(_shape)
        {
        }
 
        ShapeAllAspects& operator=(const ShapeAllAspects& _c) = default;
    };
    
 
    class Config2D_Static:public models::Config2D
    {
    public:
        template<typename FaceShape>
        using ShapeParent = ShapeAllAspects<FaceShape>;
    };
 
    typedef models::Circle<Config2D_Static> Circle;
    typedef models::Square<Config2D_Static> Square;
}
 
namespace renderers_static {
 
    template<typename Shape, typename Renderer>
    class Converter {
    public:
        void render(Shape& _shape, Renderer& _renderer)
        {
            typename Renderer::Data data;
            //заполняем data
            _renderer.render(data);
        }
    };
    template<typename Renderer>
    class Converter<views_static::Circle, Renderer> {
    public:
        static void render(views_static::Circle& _shape, Renderer& _renderer)
        {
            DRAW_FUNC(_shape, "Converter<models::Circle, Renderer>",
                std::string("render(models::Circle&, ") + typeid(_renderer).name() + "&)");
 
            typename Renderer::Data data;
            //заполняем data
            _renderer.render(data);
        }
    };
    template<typename Renderer>
    class Converter<views_static::Square, Renderer> {
    public:
        static void render(views_static::Square& _shape, Renderer& _renderer)
        {
            DRAW_FUNC(_shape, "Converter<models::Square, Renderer>",
                std::string("render(models::Square&, ") + typeid(_renderer).name() + "&)");
 
            typename Renderer::Data data;
            //заполняем data
            _renderer.render(data);
        }
    };
 
 
    template<typename ActualRenderer>
    class UniversalRendererEngine {
    public:
        template<typename Shape>
        void render(Shape& _shape)
        {
            Converter<Shape, ActualRenderer>::render(_shape, static_cast<ActualRenderer&>(*this));
        }
    };
 
    class OpenGLRendererEngine :public UniversalRendererEngine<OpenGLRendererEngine> {
    public:
        using UniversalRendererEngine<OpenGLRendererEngine>::render;
        class Data {
        public:
        };
        void render(Data&)
        {
            DRAW_FUNC(*this, "OpenGLRenderEngine", "render(const Data&)");
            std::cout << std::endl;
        }
    };
    class DirectXRendererEngine :public UniversalRendererEngine<DirectXRendererEngine> {
    public:
        using UniversalRendererEngine<DirectXRendererEngine>::render;
        class Data {
        public:
        };
        void render(Data&)
        {
            DRAW_FUNC(*this, "DirectXRenderEngine", "render(const Data&)");
            std::cout << std::endl;
        }
    };
}
 
namespace views_dynamic
{
    template<typename Config>
    class ShapeTypeDrawer {
    protected:
        typename Config::ShapeType type;
 
    protected:
        ShapeTypeDrawer(typename Config::ShapeType _type):type(_type){}
    
    public:
        typename Config::ShapeType getType()const
        {
            return type;
        };
 
        template<typename Renderer>
        void draw(Renderer& _renderer)
        {
            DRAW_FUNC(*this, "ShapeBase", "draw(Renderer&)");
            _renderer.render(*this);
        } // Перерисовать фигуру
    };
    template<typename Config, typename FaceShape>
    class ShapeBase : public models::Shape<Config, FaceShape>, public ShapeTypeDrawer<Config>
    {
    protected:
        // Защищенная часть класса, доступная только
        // выведенным из него классам
        template<typename FaceShape>
        ShapeBase(typename Config::ShapeType _type, FaceShape& _face) :
            models::Shape<Config, FaceShape>(_type, _face),
            ShapeTypeDrawer<Config>(_type)
        {
        }
    };
 
    class Config2D_Dynamic :public models::Config2D
    {
    public:
        template<typename Config, typename FaceShape>
        using BaseShape = ShapeBase<Config, FaceShape>;
 
        typedef ShapeTypeDrawer<Config2D_Dynamic> Shape;
        typedef models::Circle<Config2D_Dynamic> Circle;
        typedef models::Square<Config2D_Dynamic> Square;
    };
 
    
}
 
namespace renderers_dynamic
{
    typedef views_dynamic::Config2D_Dynamic Config2D;
 
    class IRenderer {
    public:
        virtual void render(Config2D::Shape& _shape) = 0;
    };
 
    class OpenGLRenderer : public IRenderer
    {
    public:
        virtual void render(Config2D::Shape& _shape) override
        {
            DRAW_FUNC(_shape, "OpenGLRenderer", "render(Shape& _shape)");
            switch (_shape.getType())
            {
            case Config2D::ShapeType::Circle:
                render(static_cast<Config2D::Circle&>(_shape));
                break;
            case Config2D::ShapeType::Square:
                render(static_cast<Config2D::Square&>(_shape));
                break;
            default:
                assert(false);
                break;
            }
        }
 
    protected:
        void render(Config2D::Circle& _shape)
        {
            DRAW_FUNC(_shape, "OpenGLRenderer", "render(Circle& _shape)");
            std::cout << std::endl;
        }
        void render(Config2D::Square& _shape)
        {
            DRAW_FUNC(_shape, "OpenGLRenderer", "render(Square& _shape)");
            std::cout << std::endl;
        }
    };
 
    class DirectXRenderer : public IRenderer
    {
    public:
        virtual void render(Config2D::Shape& _shape) override
        {
            DRAW_FUNC(_shape, "DirectXRenderer", "render(Shape& _shape)");
            switch (_shape.getType())
            {
            case Config2D::ShapeType::Circle:
                render(static_cast<Config2D::Circle&>(_shape));
                break;
            case Config2D::ShapeType::Square:
                render(static_cast<Config2D::Square&>(_shape));
                break;
            default:
                assert(false);
                break;
            }
        }
 
    protected:
        void render(Config2D::Circle& _shape)
        {
            DRAW_FUNC(_shape, "DirectXRenderer", "render(Circle& _shape)");
            std::cout << std::endl;
        }
        void render(Config2D::Square& _shape)
        {
            DRAW_FUNC(_shape, "DirectXRenderer", "render(Square& _shape)");
            std::cout << std::endl;
        }
    };
}
 
 
namespace
{
    template<typename Func, std::size_t I = 0, typename... Tp>
    inline typename std::enable_if<I == sizeof...(Tp), void>::type
        for_each(std::tuple<Tp...>& /*t*/, const Func& /*_f*/)
    { }
 
    template<typename Func, std::size_t I = 0, typename... Tp>
    inline typename std::enable_if<I < sizeof...(Tp), void>::type
        for_each(std::tuple<Tp...>& t, const Func& _f)
    {
        _f(std::get<I>(t));
        for_each<Func, I + 1, Tp...>(t, _f);
    }
 
}
 
int main()
{
    {
        std::cout << "=================== STATIC POLIMORFISM ===================" << std::endl;
        views_static::Circle circle;
        views_static::Square square;
 
        renderers_static::OpenGLRendererEngine openGLRenderer;
        renderers_static::DirectXRendererEngine directXRenderer;
 
        circle.draw(openGLRenderer);
        square.draw(openGLRenderer);
        
        circle.draw(directXRenderer);
        square.draw(directXRenderer);
    }
 
    {
        std::cout << "=================== STATIC POLIMORFISM with tuples ===================" << std::endl;
        auto shapes = std::make_tuple(
            views_static::Circle(),
            views_static::Square()
        );
        auto renderers = std::make_tuple(
            renderers_static::OpenGLRendererEngine(),
            renderers_static::DirectXRendererEngine()
        );
 
        for_each(renderers, [&shapes](auto& _renderer) {
            for_each(shapes, [&_renderer](auto& _shape) {
                _shape.draw(_renderer);
            });
        });
 
    }
 
    {
        std::cout << "=================== RUNTIME POLIMORFISM ===================" << std::endl;
 
        std::shared_ptr<renderers_dynamic::IRenderer> renderers[] = {
            std::make_shared<renderers_dynamic::OpenGLRenderer>(),
            std::make_shared<renderers_dynamic::DirectXRenderer>()
        };
        std::shared_ptr<views_dynamic::Config2D_Dynamic::Shape> shapes[] = {
            std::make_shared<views_dynamic::Config2D_Dynamic::Circle>(),
            std::make_shared<views_dynamic::Config2D_Dynamic::Square>()
        };
 
        for (auto& rend : renderers)
        {
            for (auto& sh : shapes)
            {
                sh->draw(*rend);
            }
        }
    }
    
    return 0;
}


Составил каждую фигуры из классов по принципу Модель-Представление.
Использовал статическую сборку фигуры из указанных компонент с использованием шаблонов.
Получилось интересно

Ну и заодно демонстрация статического и динамического полиморфизма в действии.

Вообще, где возможно, надо статически разрешать проблемы. Это очень быстрое решение всегда.
Динамическое решение всегда медленнее, но чаще удобнее.
0
Модератор
Эксперт функциональных языков программирования
3141 / 2289 / 469
Регистрация: 26.03.2015
Сообщений: 8,920
16.09.2016, 17:35
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Рендеры знают про набор суб-примимтивов из которых состоят шейпы.
А если надо выводить на экран не Квадраты, а, например, Заказы?
У Вас частный случай общей задачи. Есть промежуточный язык/формат/протокол, поэтому задача сразу решается сама собой: один класс переводит данные в этот формат, а другой - из этого формата. В общем случае так не получится.
0
 Аватар для Fulcrum_013
2083 / 1576 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
16.09.2016, 17:48
Цитата Сообщение от EVP Посмотреть сообщение
Динамическое решение всегда медленнее, но чаще удобнее.
Скорость индерект вызова не настолько уж и сильно ниже скорости просто вызова. Но при этом обычно VMT сидит в кеше в отличие от самих инстансов, что вообще разницу в скорости сводит к нулю.
Цитата Сообщение от EVP Посмотреть сообщение
Вообще, где возможно, надо статически разрешать проблемы. Это очень быстрое решение всегда.
Статический полиморфизм не позволяет запихнуть объекты разных типов в один массив и обрабатывать их при этом полиморфно.

Добавлено через 1 минуту
Цитата Сообщение от EVP Посмотреть сообщение
Ну и заодно демонстрация статического и динамического полиморфизма в действии.
Ну да. Только у вас он не статический а параметрический - на свичах. Т.е. костыльная тормозная эмуляция динамического.

Добавлено через 7 минут
Цитата Сообщение от Shamil1 Посмотреть сообщение
В общем случае так не получится.
В общем случае именно так и делается. Именно с этого и называется любой анализ предметной области - определение общих абстрактных кирпичиков из котороых все собирается. Яванцев и питонцев обычно правда этому не учат. Это не их квалификации и ума дело.
Цитата Сообщение от Shamil1 Посмотреть сообщение
а, например, Заказы
Заказ тоже состоит из каких-то суб-объектов (обычно из записей заказанных итемов), записи тоже состоят из полей. Набор типов полей ограничен по своей природе.
0
510 / 272 / 60
Регистрация: 14.12.2010
Сообщений: 548
16.09.2016, 17:58
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Скорость индерект вызова не настолько уж и сильно ниже скорости просто вызова. Но при этом обычно VMT сидит в кеше в отличие от самих инстансов, что вообще разницу в скорости сводит к нулю.
Нет, разница в скорости огромна.
Если компилятор может статически вызовы разобрать, он начинает встраивать вызовы.
При этом никаких шансов у динамического вызова нет по скорости исполнения цепочек таких вызовов.
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Статический полиморфизм не позволяет запихнуть объекты разных типов в один массив и обрабатывать их при этом полиморфно.
Для этого есть std::tuple<>. Но потребуется очень много памяти для разрешения всех статических ветвлений.
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Только у вас он не статический а параметрический - на свичах. Т.е. костыльная тормозная эмуляция динамического.
Да я вывернул исходное решение, чтобы не повторяться.
0
 Аватар для Fulcrum_013
2083 / 1576 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
16.09.2016, 18:30
Цитата Сообщение от EVP Посмотреть сообщение
Если компилятор может статически вызовы разобрать, он начинает встраивать вызовы.
А если при этом компилятор тянет данные инстансов методы которых вызывает в кеш из ОЗУ то сидеть ему все равно ждать поступления данных циклов 500 при любом полиморфизме.

Добавлено через 26 секунд
Цитата Сообщение от EVP Посмотреть сообщение
Для этого есть std::tuple<>. Но потребуется очень много памяти для разрешения всех статических ветвлений.
т.е. комбинаторный взрыв гарантирован.

Добавлено через 3 минуты
Цитата Сообщение от EVP Посмотреть сообщение
При этом никаких шансов у динамического вызова нет по скорости исполнения цепочек таких вызовов.
То у статического никаких шансов нет. К примру потому как код даже очень сложных систем на динамическом полиморфизме способен полностью жить в L2. Для аналогичного кода на статическом и L3 не хватит ни под каким соусом. А это на скорость влияет гораздо больше чем direct или indirect вызов. при этом любой лонг джамп сбрасывает кеш L1. соответственно инлайнить код больше 256 байт никакого смысла не имеет.

Добавлено через 1 минуту
Цитата Сообщение от EVP Посмотреть сообщение
Но потребуется очень много памяти для разрешения всех статических ветвлений.
т.е. будет костыльный тормозной набор свичей/ифов.

Добавлено через 4 минуты
Цитата Сообщение от EVP Посмотреть сообщение
Нет, разница в скорости огромна.
Ну да. Вообще то вызова подпрограмм это тормознуто и надо избегать использования подпрограмм. Это во всех учебниках по фортрану написано еще в 50-60х годах прошлого века.

Добавлено через 13 минут
Цитата Сообщение от EVP Посмотреть сообщение
Нет, разница в скорости огромна.
Если компилятор может статически вызовы разобрать, он начинает встраивать вызовы.
При этом никаких шансов у динамического вызова нет по скорости исполнения цепочек таких вызовов.
То что к примеру по скорости рандомного удаление/вставки элемента у вектора против двусвязного списка нет шансов алгоритмически доказано сто раз и известно абсолютно всем, кроме микропрцессоров которые утверждают абсолютно обратное. А все потому как без предварительного линейного поиска оная вставка/удаление смысла не имеет, а именно при линейном поиске вектор кеш пользует на полную а двусвязный список чуть менее чем никак.
0
Модератор
Эксперт функциональных языков программирования
3141 / 2289 / 469
Регистрация: 26.03.2015
Сообщений: 8,920
16.09.2016, 18:49
У меня сейчас нет времени расписывать аргументы - позже напишу. А сейчас просто скажу, что Ваш вариант очень прост и не требует использования ООП. Вот код на Хаскеле - как всегда в 5 раз короче.
Haskell
1
2
3
4
5
6
7
8
data Shape = Circle Double Double Double 
           | Square Double Double Double 
 
draw (Circle r x y) f = print $ f "circle"
draw (Square s x y) f = print $ f "square"
 
screen s = s ++ " on screen"
cutter s = s ++ " on laser cutter"
0
 Аватар для Fulcrum_013
2083 / 1576 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
16.09.2016, 18:54
Цитата Сообщение от Shamil1 Посмотреть сообщение
меня сейчас нет времени расписывать аргументы - позже напишу. А сейчас просто скажу, что Ваш вариант очень прост и не требует использования ООП.
А теперь давайте на задачу смотреть комплексно - не просто отрисовка одного шейпа а комплекса - чертежа из взаимосвязанных шейпов. При этом поддержка всяческих манипуляций, взаимосвязей и т.п. в том числе визуального редактирования параметров оных шейпов и сохранения привязок/размеров шейпов при изменении параметров одного/группы шейпов.
0
Модератор
Эксперт функциональных языков программирования
3141 / 2289 / 469
Регистрация: 26.03.2015
Сообщений: 8,920
16.09.2016, 18:58
Цитата Сообщение от Igor3D Посмотреть сообщение
А я все думаю - ну когда же будет изумительный хаскель?
Хаскель, действительно, изумительный и красивый язык. Это следствие продуманного синтаксического сахара.
Например, там, где на другом языке мне надо писать "(x => x + 4)" на Хаскеле я могу написать "(+4)".
К сожалению, Хаскель не является лучшим функциональным языком. Есть у него свои недостатки. Сейчас, пожалуй, лучший (наиболее перспективный) - это F#... но никак не могу себя заставить его учить (хотя уже около 60 небольших программ на нём написал).

Добавлено через 2 минуты
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
А теперь давайте на задачу смотреть комплексно
Пишите упрощённо код абстракции. Полагаю, что перевести его на хаскель труда не составит.
0
 Аватар для Fulcrum_013
2083 / 1576 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
16.09.2016, 19:28
Цитата Сообщение от Shamil1 Посмотреть сообщение
Хаскель, действительно, изумительный и красивый язык. Это следствие продуманного синтаксического сахара.
Сахар сахаром а моделирование поведения и состояния объектов моделированием поведения и состояния. Это в любом ФП языке затруднено.

Добавлено через 3 минуты
Цитата Сообщение от Shamil1 Посмотреть сообщение
Пишите упрощённо код абстракции. Полагаю, что перевести его на хаскель труда не составит.
Первое что понадобится для такой задачи это слежение контейнера за жизненным циклом содержимого. В языках с автоматической сборкой мусора это всегда гемморойное дело.

Добавлено через 11 минут
Цитата Сообщение от Shamil1 Посмотреть сообщение
Вот код на Хаскеле - как всегда в 5 раз короче.
В мозги не пудрите. Строчки поскладывать на С++ тоже гораздо коротче. А вот полиморфно вызываемого кода который напечатает эти "on screen" или "on laser cutter" (которые в реальном коде меняются на алгоритмы отрисовки суб-примитива на девайсе) что то не видать в ваших хаскелях.

Добавлено через 4 минуты
Цитата Сообщение от Shamil1 Посмотреть сообщение
Полагаю, что перевести его на хаскель труда не составит.
В конечном итоге придете к тому же динамическому полиморфизму и VMT только собирать их на хаскеле придется ручками. Да и состояния объектов средствами ФП изменять тот еще геморой будет.

Добавлено через 5 минут
Цитата Сообщение от Igor3D Посмотреть сообщение
Все это у разных дивайсов разное, полагать что OutText "просто так" даст нужный рез-т, мягко говоря, наивно
Запросто. Все координаты в физических единицах (в мм). Преобразования в логические единицы девайса осуществляет контекст. Посему переключение стилей отрисовки тоже унифицируется.
0
510 / 272 / 60
Регистрация: 14.12.2010
Сообщений: 548
16.09.2016, 19:52
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
т.е. будет костыльный тормозной набор свичей/ифов.
Набора свитчей/условий не будет, т.к. всегда будет известен тип статически.
Просто каждый новый вызываемый метод должен быть шаблонным, что порождает много ветвлений.
Поэтому упираемся очень быстро в память. Да, комбинаторный взрыв по памяти.
Это к вопросу об эквивалентности статического и времени исполнения полиморфизмов.
(Я пример приводил, где статически все вызовы разрешены. Так делать можно, но сложно.)

Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Вообще то вызова подпрограмм это тормознуто и надо избегать использования подпрограмм. Это во всех учебниках по фортрану написано еще в 50-60х годах прошлого века.
Поэтому шаблонные вызовы компилятор может встраивать или полностью устранять.
А если появился динамический вызов, то как его обойдёшь во время компиляции? Никак.
Потому и тормозить будет не только указателем, но и накладными расходами на вызов и
его последствиями для компиляции. Это существенно для числодробилок.

ЗЫ: Ничего против полиморфизма времени исполнения не имею,
т.к. идеальной реализации на все случае жизни в C++ пока нет.
0
 Аватар для Fulcrum_013
2083 / 1576 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
16.09.2016, 20:24
Цитата Сообщение от EVP Посмотреть сообщение
росто каждый новый вызываемый метод должен быть шаблонным, что порождает много ветвлений.
В динамическом полиморфизме никаких ветвлений нет.

Добавлено через 2 минуты
Цитата Сообщение от EVP Посмотреть сообщение
Поэтому шаблонные вызовы компилятор может встраивать или полностью устранять.
при переходе дальше 256 байт кеш сбрасывается. Поэтому экономить на вызовах там где это не нужно не просто без толку, вредно с точки зрения быстродействия.

Добавлено через 8 минут
Цитата Сообщение от EVP Посмотреть сообщение
Потому и тормозить будет не только указателем, но и накладными расходами на вызов и
его последствиями для компиляции.
Накладные расходы на вызов не существенны по сравнению с промахом кеша. Больший по размеру код будет иметь большее количество промахов кеша инструкций. При этом промахи к данным одинаковы для обоих типов, а инлайнинье больше определенного предела вызывает тормоза, т.е. комбинаторный взрыв количества потребных переходов, при этом переходы тянут не меньшие накладные расходы чем вызова. Именно поэтому компилятор не гарантирует что будет инлайнить функции помеченные как инлайн. Кстати частенько оптимизация заключается именно в замене ветвления на таблицу неявных переходов. К примеру таким способом (таблицей адресов переходов) по возможности компилятор оптимизирует свичи, а не конвертирует их в лестницу ифов.

Добавлено через 1 минуту
Цитата Сообщение от EVP Посмотреть сообщение
А если появился динамический вызов, то как его обойдёшь во время компиляции?
Набор ифов для его эмуляции гораздо тормознее.

Добавлено через 4 минуты
При этом не стоит забывать что VMT тоже данные и тоже кешируются. Так же как и указатель на VMT является данными инстанса. т.е. в результате основной задержкой является никак не тип вызова и даже не сам вызов, а доступ к данным инстанса который и там и там тянет на одинаковую задержки если его нет в кеше. При этом учитывая что в большинстве случаев данные инстанса больше чем линия кеша, то остается только уповать на телепатора-предсказателя потребного предкешиования в обоих случаях.

Добавлено через 3 минуты
Цитата Сообщение от EVP Посмотреть сообщение
Это существенно для числодробилок.
Числодробилки давно числодробят на GPGPU. Там в этом плане совсем другая песня. Там даже константу к каждому элементу массива добавляют запуская по потоку на каждый элемент.

Добавлено через 9 минут
Цитата Сообщение от EVP Посмотреть сообщение
Просто каждый новый вызываемый метод должен быть шаблонным, что порождает много ветвлений.
А как вы без ветвлений определите какой именно инстанционированный метод вызывать (ну или на какую заинлайненную ветку переходить) если реальный тип не известен в момент компиляции? либо индирект вызов по адресу из VMT либо свитч по typeid. третьего не дано.
0
510 / 272 / 60
Регистрация: 14.12.2010
Сообщений: 548
16.09.2016, 21:31
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Сообщение от EVP: А если появился динамический вызов, то как его обойдёшь во время компиляции?
Набор ифов для его эмуляции гораздо тормознее.
Почему сравниваешь с ифами, если можно сравнить с вариантом с кортежами?
Посмотри пример варианта с кортежами ещё раз. Там всё статически и без вариантов.
Ладно, ты похоже фанат динамики... оставлю как есть.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
16.09.2016, 21:31

ООП пример
Доброго времени суток. Тут отыскался один пример в конспекте. Записал на лекции спустя рукава. Хочу восстановить. Где-то что-то...

Пример ООП на D
Пример из книги Язык программирования D Андрей Александреску стр.49 1.6. Интерфейсы и классы Даны числа, нужно найти минимум,...

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

Нужен пример использования ООП
Вот ООП в JavaScript есть, но каким боком его можно использовать? Я еще не сталкивался с такими вещами, в которых процедурно-функциональный...

Нужен пример с использованием ООП
Дарова всем, хотел спросить вас, может кто знает ссылку на инфу о объект - ориентированном программировании в Visual Basic? Я уже весь инет...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Запустил конкурс "тем и промптов для текстовых квестов созданных почти чисто ИИ"
Adler 06.10.2026
Всем привет! За последние три-четыре дня я создал более 16 текстовых квестовых игр используя преимущественно по одному запросу к ИИ на игру. Мне так понравилось смотреть все ветки/ сцены во всех. . .
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык. Возвращает он вот это: https:/ / i. **********/ vqBWLe2. png Нужную строчку в 3й колонке просто выдумал. . Это. . .
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor. Это. . .
Nekobox - outbounds[0].transport: unknown transport type: raw
damix 01.10.2026
Фикс ошибки Правым кликом по серверу -> отладочная информация -> edit Заменить "net": "raw", на "net": "tcp", Нажать кнопку reload.
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru