859 / 448 / 112
Регистрация: 06.07.2013
Сообщений: 1,491
1

Обойтись без наследования

26.10.2019, 19:54. Показов 3889. Ответов 18
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Всем привет, сейчас очень часто вижу в интернете мнение что наследование ни к чему хорошему не приводит, от него одни проблемы, кроме того в таких языках как Rust и Go его в принципе нет. Немного поизучав Rust и распрашивая его community получил несколько ответов на то как же обходиться без наследования там где хотелось бы, из вариантов ответа 2 самых популярных это использование ECS (Entity component system) и композиция.

Теперь собственно пример

имеем класс (экран который хотелось бы при показе на дисплей активировать для загрузки ресурсов в первый раз) на С++
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Screen
{
 private:
    bool active = false;
 
  public:
    void activate()
    {
      if(active)
        return;
 
      active = true; 
      onActivate();
    }
 
  protected:
    virtual void onActivate() {}
}
имея данный класс как базовый, мы можем унаследовать от него другие классы

C++
1
2
3
4
5
class ScreenMainmenu : public Screen
{
  protected:
    void onActivate() override {// реализация метода при загрузке/первом показе экрана}
}
просто, удобно и понятно, менеджер экранов вызвает activate() у экрана и не парится, а так же каждый новый созданный экран может не содержать у себя bool active, проверку на флаги итд, берет и оверрайдит onActivate

Как же тогда сделать такое без наследования дабы понять как это все перенести в модерн языки типа Rust? (может быть кто то знаком с Rust и Go и понимает как это элегантно спрототипировать хотя бы на том же C++)

вот к чему прихожу я:
первый вариант -
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
// подобие trait в Rust
// здесь нельзя иметь поля
struct Screen
{
    virtual void onActivate() = 0;
}
 
struct ScreenMainmenu : public Screen
{
    void onActivate() override
    {
        // реализация 
    }
}
 
struct ScreenManager
{
    bool active[NUM_SCREENS]; // флаги на все экраны
    Screen* screens[NUM_SCREENS]; // указатели на все экраны
    
    void activate(EScreen screenID)
    {
        if(active[screenID])
            return;
        
        active[screenID] = true;
        screens[screenID].onActivate();
    }
}
Но разве это не создание неудобного и труднорасширяемого кода?

второй вариант-
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
// Делаем вообще один класс для всего
struct Screen
{
    bool active = false;
    
    void activate()
    {
        if(active)
            return;
        
        active = true;
        
        if(onActivate)
            onActivate();
    }
    
    std::func<void(Screen*)> onActivate = nullptr;
}
 
// Создаем реализацию
 
void ScreenMainMenu_OnActivate(Screen* THIS)
{
    // реализация
}
 
Screen screenMainmenu;
screenMainmenu.onActivate = ScreenMainMenu_OnActivate;
но разве это не бред? попытка сделать виртуальную функцию и возвратиться обратно к старому мышлению)

Может быть я чего то не понимаю в этих новых языках программирования и нужно научиться мыслить по другому? Как бы вы поступили с данной вроде бы простой ситуацией где хотелось бы для всех экранов переиспользовать логику его активирования и не копипастить ее в каждый класс без наследования классов?
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
26.10.2019, 19:54
Ответы с готовыми решениями:

Обойтись без If ?
Доброго времени суток, уважаемые форумчане. Помогите, пожалуйста, разобраться со сложившейся...

Можно ли обойтись без if-во?
Создать структуру с полями (или класс с функциями), которые приведены в задаче. Описание структуры...

можно ли обойтись без IF ?
Нужно заполнить с главного массива(mas_g) один массив(mas1) елементы с чётными номерами, а в...

Можно ли обойтись без mutex?
в оф мануале : говориться что данные будут появляться то так то эдак (неопределенность из-за...

18
6579 / 4564 / 1843
Регистрация: 07.05.2019
Сообщений: 13,726
26.10.2019, 21:05 2
Цитата Сообщение от Raali Посмотреть сообщение
Всем привет, сейчас очень часто вижу в интернете мнение что наследование ни к чему хорошему не приводит, от него одни проблемы, кроме того в таких языках как Rust и Go его в принципе нет. Немного поизучав Rust и распрашивая его community получил несколько ответов на то как же обходиться без наследования там где хотелось бы, из вариантов ответа 2 самых популярных это использование ECS (Entity component system) и композиция.
По-моему, нет никаких проблем с наследованием. Единственная проблема - квалификация программистов, которая падает пропорционально количеству новых языков, типа Rust и Go.
Ты здесь показал три варианта решения задачи, в общем случае они равнозначные, ни один не лучше и не хуже другого. У каждого свои достоинства и свои недостатки.
Поэтому основная проблема здесь - выбрать вариант, который наилучшим образом подходит для решения конкретной задачи. Вот это, как правило, действительно сложно, и этому надо учится.
Подозреваю, в новых языках тупо нет этого выбора и большинству это нравится - не надо думать, за тебя уже всё сделали.
1
859 / 448 / 112
Регистрация: 06.07.2013
Сообщений: 1,491
26.10.2019, 21:15  [ТС] 3
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
не надо думать, за тебя уже всё сделали.
Так то оно конечно так, но почему то кажется в новых языках что то привычное сделать в 2 раза геморнее получается или нужна привычка
0
6579 / 4564 / 1843
Регистрация: 07.05.2019
Сообщений: 13,726
26.10.2019, 21:21 4
Цитата Сообщение от Raali Посмотреть сообщение
Так то оно конечно так, но почему то кажется в новых языках что то привычное сделать в 2 раза геморнее получается или нужна привычка
По моим наблюдениям, прежде всего нужна низкая квалификация, как программиста. Без этого программировать на них тяжеловато.
0
Комп_Оратор)
Эксперт по математике/физике
8948 / 4702 / 629
Регистрация: 04.12.2011
Сообщений: 13,999
Записей в блоге: 16
27.10.2019, 11:24 5
Raali, у наследования есть ряд недостатков, но они - плата за преимущество. Если вам не нужно работать с указателем на базовый класс (разрешение типа во время выполнения или динамический полиморфизм), то использовать наследование - искать мрачного будущего. Трудности состоят в отсутствии информации на уровне базового класса о расширяющих возможностях наследника. Добавление новой функциональности приводит к комбинаторному росту сложности. Нельзя сменить объект во время выполнения.
Можно использовать возможности статического полиморфизма. Перегрузки функций, шаблоны функций, шаблоны классов. У Александреску есть описания обобщённого подхода к реализации таких паттернов как стратегия, например. При реализации через шаблоны, теряется возможность динамического полиморфизма, но зато легче и проще комбинировать функционал отогональных алгоритмов (простите за отсебятину). Разделение функционала (ортогонизация) для выявления стратегий - достаточно сложная штука, требующая опыта и времени. Raali, а готовых решений на ваш случай нет?
0
859 / 448 / 112
Регистрация: 06.07.2013
Сообщений: 1,491
27.10.2019, 15:07  [ТС] 6
Цитата Сообщение от IGPIGP Посмотреть сообщение
Raali, а готовых решений на ваш случай нет?
Я думаю конечно есть, и готовые игровые движки и подобие каких то сцен с экранами на том же Rust.
Но мне никак не вникнуть в образ мышления и как бы я решал такие вопросы когда нужно самому построить архитектуру приложения.
вот кстати ради справки, спросил на другом источнике, какой вариант там предлагают

In Rust, sometimes the solution can be something totally different that you haven't thought of. Also coming from C++, it might take some time to develop an intuition. I don't know the structure of your code, but maybe something like this makes sense here as well.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
trait Activator {
    fn activate_with<F: FnOnce()>(&mut self, activation: F);
}
 
impl Activator for bool {
    fn activate_with<F: FnOnce()>(&mut self, activation: F) {
        if !*self {
            *self = true;
            activation();
        }
    }
}
 
fn main() {
    let mut active = false;
    active.activate_with(|| println!("Activating"));
}
что здесь происходит
человек предложил расширить стандартный тип bool!
и метод бы проверял и менял *this на true
аналог на C++ был бы что то типа
C++
1
2
3
4
5
6
bool::activate_with(std::function func){
  if(!*this)
    *this = true;
     
    func();
}
у меня после этого вообще мозг вскипел)
0
872 / 459 / 90
Регистрация: 10.06.2014
Сообщений: 2,665
27.10.2019, 16:10 7
Raali,
Насколько я понял вам просто предложили использовать трейты.
С этим языком не знаком, но вообще трейты это как правило именованная группа функций (или одна функция как к вашем случае). Можно использовать имя этой группы в классе который хотите расширить, и таким образом импортировать в этот класс функции из трейта. Используя этот подход можно избежать дублирования без использования наследования.

То есть если во все экраны вы импортируете трейт-активатор, то все классы экранов получат доступ к методам которые описаны в трейте
0
859 / 448 / 112
Регистрация: 06.07.2013
Сообщений: 1,491
27.10.2019, 18:30  [ТС] 8
Цитата Сообщение от Undisputed Посмотреть сообщение
Насколько я понял вам просто предложили использовать трейты.
Ага советуют то много) но никто не может дать конкретной реализации как бы они поступили в данной ситуации, все абстрактно так

Получаем композицию?
или я не так понял

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
struct ActivatorTrait
{
    virtual void activate(std::function f) = 0;
}
 
struct Activator : public ActivatorTrait
{
    bool active = false;
    
    void activate(std::function f) override
    {
        if(active)
            return;
        
        active = true;
        
        f();
    }
}
 
struct Screen
{   
    virtual void activate();
}
 
struct ScreenMainmenu : public Screen
{
    Activator activator;
    
    // в таких местах снова для каждого экрана нужно будет копировать код ) 
    // хоть меньше но придется
    void activate() override
    {
        activator.activate([this]{this->onActivate});
    }
    
    void onActivate()
    {
        
    }
}
вообщем пока не могу представить как можно полностью перейти на Rust с плюсов, если простая задача требует таких танцев
0
872 / 459 / 90
Регистрация: 10.06.2014
Сообщений: 2,665
27.10.2019, 20:27 9
Raali,
Не, решение на трейтах это не композиция. У вас аналогия получилась некорректная потому что трейт это абстрактный тип, экземпляр которого не создаётся. Но трейт может быть использован для расширения функционала классов.

Просто повторяющийся для всех классов метод/методы можно вынести в трейт (вместо композиции)...
А потом можно расширить функционал ScreenMainmenu посредством трейта который "внесет" дополнительный функционал в класс ScreenMainmenu, как если бы этот функционал был бы описан напрямую в классе ScreenMainmenu а не в трейте. Так обычно работают трейты. По аналогии можно сделать и для остальных классов, которым нужен функционал описанный в трейте.
0
859 / 448 / 112
Регистрация: 06.07.2013
Сообщений: 1,491
27.10.2019, 22:01  [ТС] 10
Цитата Сообщение от Undisputed Посмотреть сообщение
Просто повторяющийся для всех классов метод/методы можно вынести в трейт (вместо композиции)...
Но проблема в том что в трейт я не смогу добавить bool флаг, соответственно выполнить проверку на условие
0
Комп_Оратор)
Эксперт по математике/физике
8948 / 4702 / 629
Регистрация: 04.12.2011
Сообщений: 13,999
Записей в блоге: 16
27.10.2019, 22:18 11
Цитата Сообщение от Raali Посмотреть сообщение
Но проблема в том что в трейт я не смогу добавить bool флаг, соответственно выполнить проверку на условие
Он и так не в трейте. Он в объекте mut и трейт (нечто вроде функтора или своеобразного биндера) извлекает этот bool из параметра пришедшего по ссылке на объект mut. Что тут сложно реализовать? Я видимо не понимаю вопроса. Raali, ещё непонятно зачем наследование, действительно, если в ScreenMainmenu содержится не указатель на ActivatorTrait а объект наследника?
И что это за синтаксис:
[this]{this->onActivate}
?
1
859 / 448 / 112
Регистрация: 06.07.2013
Сообщений: 1,491
27.10.2019, 22:45  [ТС] 12
Цитата Сообщение от IGPIGP Посмотреть сообщение
И что это за синтаксис:
[this]{this->onActivate}
?
поторопился, имелось в виду что передается лямбда
C++
1
[this]{this->onActivate();}
Цитата Сообщение от IGPIGP Посмотреть сообщение
Он в объекте mut и трейт (нечто вроде функтора или своеобразного биндера) извлекает этот bool из параметра пришедшего по ссылке на объект mut. Что тут сложно реализовать?
вот про параметр не подумал, спасибо, теперь более менее вижу нормальный вариант

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
struct ActivatorTrait
{
    // дефолтная реализация для переиспользования кода
    virtual void activateWithFlag(bool& flag)
    {
        if(flag)
            return;
        
        flag = true;
        onActivate();
    }
    
        // метод для пользователя
    virtual void activate() = 0;
    
    virtual void onActivate() = 0;
}
 
struct Screen
{
    bool active = false;
}
 
struct ScreenMainmenu : public ActivatorTrait
{
        // композиция
    Screen screen;
    
    void activate() override
    {
        // переиспользуем код
        activateWithFlag(screen.active);
    }
    
    void onActivate() override
    {
        
    }
}
0
872 / 459 / 90
Регистрация: 10.06.2014
Сообщений: 2,665
27.10.2019, 23:24 13
Raali,
Если в Rustовских трейтах поля не поддерживаются, то думаю композиция это хорошее решение. Ну или раздать каждому скрину по флагу, которым будут манипулировать методы трейта (но этот вариант мне совсем не нравится)
1
Комп_Оратор)
Эксперт по математике/физике
8948 / 4702 / 629
Регистрация: 04.12.2011
Сообщений: 13,999
Записей в блоге: 16
28.10.2019, 11:26 14
Raali, с логикой переключения состояния мы вроде разобрались. Нет смысла реактивировать то, что уже активировано как скажем, и перерисовывать области, которые не изменялись с момента последней перерисовки (хотя вот тут более сложная логика :-) ). Хочу выступить в защиту того, что и без меня неплохо защищено, а именно, - наследования. Я не вижу схемы взаимодействий и поэтому не могу сказать определённо, но мысля по аналогии:
Screen - вероятно класс отвечающий за наиболее общие свойства окна. То есть, как субъект клиентской области он отвечает за обработку событий - нажатие клавиши при активном состоянии, клик мышки на области, нажатие группы горячих клавиш. При чем, не важно что инициатором может являться родитель на области которого хостится клиент. Важно именно то, что ответственность лежит на нём - хранится состояние. Остальные вещи - навродь, цветов фона/шрифта... , и пр. тоже там же.
Но тогда, просматривается схема взаимодействия в которой главное окно, - работающее с родительским процессом (собственником цикла обработки сообщений) в своём потоке - но главном потоке процесса, дспетчеризует и отправляет сообщения потомкам. В этом случае, работа с контейнером указателей базового класса (на потомков), вполне логична. Но это значит что Screen должен быть членом иерархии.
Но если он и есть родитель, то наверное, состояние (символизируемое логической переменной) лучше перенести в потомка, то есть туда, где это состояние реализовано и где нужно знать о том, в каком состоянии потомок находится. Иначе, придётся хранить контейнер состояний, сопряженный с объектами по индексам, например.
Raali, я часто слышу старые остроты про ООП головного мозга. Но мир это набор процессов. Тут объекты, - это роли. То есть, в группе рассматриваемых для описания модели процессов, выделяются такие, что их характеристики логически связаны и часть из них условно постоянна. Например, блюдечко и молоко в нём, это объекты, котёнок тоже. А свет в комнате, это функция. Она тоже может стать элементом поведения комнаты, но это не всегда нужно для описания. Например, может быть не важно, это солнечный свет или от люстры, или от настенного светильника. Потому, что это за пределами модели и нет смысла заранее усложнять её. Почему же мы выделяем ряд процессов в объекты? Наверное потому, что наш мозг не способен отслеживать много параметров одновременно. Из-за этого мы ищем, более мене постоянные, узнаваемые (идентифицируемые), характерные (присущие подобным моделям) фрагменты и делаем их узловыми в модели, чтобы иметь опорные точки для построения суждений, - логических фрагментов описания.
Подытоживая, - объектная модель, это просто модель. Нельзя построить не объектную модель. А строить модели (описывать мир и хранить систематизированные знания), это свойство мозга. Вот и выходит, что если есть мозг, в нём есть ООП. И чем больше тем больше, но верно и обратное. С++ тут лишь часть общей картины.
0
Комп_Оратор)
Эксперт по математике/физике
8948 / 4702 / 629
Регистрация: 04.12.2011
Сообщений: 13,999
Записей в блоге: 16
30.10.2019, 23:07 15
Raali, вот сумбурно, без шлифовки (форвардинга ссылок, виртуальных деструкторов и пр.) такой себе каламбур. Но он хоть компилируется (в отличие от некоторых примеров) и даже чёто выводит.
Обойтись без наследования не получается, но комбинировать функционал с большей гибкостью чем при множественном наследовании можно.
Кликните здесь для просмотра всего текста

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
#include <iostream>
using namespace std;
 
template<class T>
struct ActivatorFromFile
{
 
  void activate(T &activated)
  {
      activateFromFile(activated);
  }
  void  activateFromFile(T &activated)
  {
      activated.active(true);
      cout<<"\nactivateFromFile(T &activated)\n";
  }
 
};
 
template<class T>
struct ActivatorFromNet
{
  void activate(T &activated)
  {
      activateFromNet(activated);
  }
  void  activateFromNet(T &activated)
  {
      activated.active(true);
      cout<<"\nactivateFromNet(T &activated)\n";
  }
};
 
template<class T>
struct ActivatorFromOrigin
{
    T origin;
    ActivatorFromOrigin(const T& _origin):origin(_origin){}
 
  void activate(T &activated)
  {
      activateFromOrigin(activated);
  }
  void  activateFromOrigin(T &activated)
  {
      if(!activated.active()) activated.active(true);
      cout<<"\nactivateFromOrigin(T &activated)\n";
 
  }
};
 
 
struct Screen
{
    bool active = false;
    //other data of screen
 
};
 
struct Menue
{
 Screen scr;
 const char *name="Menue";
 bool  active(){return scr.active;}
 void active(bool is_true){scr.active=is_true; cout<<name<<endl;}
};
 
struct MainMenue
{
 Screen scr;
 const char *name="MainMenue";
 bool  active(){return scr.active;}
 void active(bool is_true){scr.active=is_true; cout<<name<<endl;}
};
 
template <template <class> class ActivationPolicy>
struct ScreenManager : public ActivationPolicy<Menue>
{
    ActivationPolicy<Menue> ap;
    ScreenManager(const ActivationPolicy<Menue>& ap_)
    :ap(ap_){}
    void activate (Menue *men)
    {
        ap.activate(*men);
    }
};
 
 
int main()
{
    Menue men, men1;
    ActivatorFromFile<Menue> aff;
    ScreenManager<ActivatorFromFile> afm(aff);
    afm.activate(&men);
    
    ActivatorFromNet<Menue> afn;
    ScreenManager<ActivatorFromNet> afk(afn);
    afk.activate(&men1);
    return 0;
}

Контрольный (конечно трольный) выстрел, так сказать. Но в каждой шутке есть доля шутки.
0
Комп_Оратор)
Эксперт по математике/физике
8948 / 4702 / 629
Регистрация: 04.12.2011
Сообщений: 13,999
Записей в блоге: 16
31.10.2019, 12:10 16
Цитата Сообщение от IGPIGP Посмотреть сообщение
виртуальных деструкторов
пересмотрел и рассмеялся. В данном наследовании нет ни одного виртуального метода. То есть delete на полиморфных указателях не грозит и в/деструкторы не нужны.
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
31.10.2019, 13:17 17
Цитата Сообщение от Raali Посмотреть сообщение
сейчас очень часто вижу в интернете мнение что наследование ни к чему хорошему не приводит, от него одни проблемы
ниосиляторское бла бла бла.

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

вот такие заявления:
Цитата Сообщение от IGPIGP Посмотреть сообщение
Если вам не нужно работать с указателем на базовый класс (разрешение типа во время выполнения или динамический полиморфизм), то использовать наследование - искать мрачного будущего.
не более чем диванная теория человеков,
которые понятия не имеют,
как их домыслы состыкуются с реальной практикой.

заметь, какие используются формулировки.
фразы вида: "искать мрачного будущего" - не содержат ответа на вопрос: "почему?"


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

в том, что касается объективной критики "реальных проблем наследования",
суть простая:
- поддержка множественного наследования - это +100500 к сложности компилятора.

- поддержка виртуального наследования - это ещё +100500 к сложности компилятора,
и ещё +100500 к сложности архитектуры проекта
с точки зрения типичного хомячка-неосилятора.

итого:
- разработчики некоторых языков отказались от множественного наследования,
что бы не поиметь проблем с ромбовидными иерархиями (что бы не реализовывать виртуальное наследование).
таким образом с одной стороны разработчикам компиляторов не нужно геммороится с поддержкой множественного,
и виртуального наследования,
а хомячкам (слабым программистам) - вдуплять в архитуктуры с ромбами.
0
Комп_Оратор)
Эксперт по математике/физике
8948 / 4702 / 629
Регистрация: 04.12.2011
Сообщений: 13,999
Записей в блоге: 16
31.10.2019, 13:49 18
Цитата Сообщение от hoggy Посмотреть сообщение
не более чем диванная теория человеков,
Это ничто против тупого хамства. Речь шла именно о открытом (раз) и полиморфном (два), наследовании. Все сказанное коротко, - вне контекста обсуждения, это глупость, так как вопросы очень громоздки и говорить о них точно, - нужно писать книги. Да и в книгах есть много мест, о которых можно и нужно спорить. Ваше хамство не удивляет. Одно не ясно. Я не обращаюсь к вашему контенту и вроде предлагал не обращать внимание на мой. Видимо это слишком много.
0
Mental handicap
1246 / 624 / 171
Регистрация: 24.11.2015
Сообщений: 2,429
31.10.2019, 18:13 19
То что есть в rust как type_traits, в go interfaces, в haskell type classes это все немного из другой оперы.
На данный момент в С++ есть два варианта полиморфизма это статический и динамический (через виртуальные методы и наследование).
То, что вы хотите это вариант динамического полиморфизма основанного на type erasure (как и в rust, etc) позволяющий так называемый duck typing.
В С++ напрямую такой поддержки нету, но есть возможность накостылить, как и сказал hoggy.
Внешне это может выглядеть так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct screen_activator {
    void activate() const noexcept {
        te::call([](auto const& self) { self.activate(); }, *this);
    }
};
 
struct fullhd_screen {
  void activate() const noexcept { std::cout << "fullhd_screen\n"; }
};
 
struct lowhd_screen {
  void activate() const noexcept { std::cout << "lowhd_screen\n"; }
};
 
void activate(te::polymorphic<screen_activator> const &a) { a.activate(); }
 
int main() {
    activate(fullhd_screen{});
    activate(lowhd_screen{});
}
(Если интересно могу показать остальной код )
Но в потрохах это будет страшная штука
Она могла бы быть менее страшной если бы type loophole не сделали ill-formed, а так пока просто используем наследование и вирт ф-и.
0
31.10.2019, 18:13
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
31.10.2019, 18:13
Помогаю со студенческими работами здесь

Как обойтись без виртуальных конструкторов?
Я перехожу с Delphi. Многое там строится на виртуальных конструкторах, как без этого обойтись или...

Функция abs и как обойтись без нее
Нужна помощь. При написании кода столкнулся с некой проблемой, что при использовании функции abs...

Можно ли обойтись без перезаписи элементов массива?
Реализую алгоритм, в котором требуется взять из массива один элемент и вставить его в другую...

Можно ли обойтись без разыменования адресов на элементы
Вот куски кода (полный код тут): // Double Bitset 85 bitset&lt;ULONG_MAX&gt; *first = new...


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

Или воспользуйтесь поиском по форуму:
19
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru