Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.89/9: Рейтинг темы: голосов - 9, средняя оценка - 4.89
36 / 37 / 10
Регистрация: 02.04.2016
Сообщений: 300
1

Как правильно группировать статические данные, создаваемые по-разному?

16.08.2018, 03:09. Показов 1846. Ответов 11

Есть у меня базовый класс:
C++
1
class A { };
Хочу обогатить его наследников статическими данными.
Тогда возникает проблема: наследников много = много повторных деклараций этих статических данных.
Хочется хранить всё в одном месте, но создавать по-разному.

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

Тогда я ввожу третий класс:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A { };
 
template <int i_value, char c_value>
class B: public A {
  public:
    inline static const int i = i_value;
    inline static const char c = c_value;
};
 
class X: public B<4, 'r'> { };
class Y: public B<6, 'g'> { };
 
int main() {
    std::cout << X().i + Y().i;
}
На простых типах это работает. Как дело касается достаточно сложных,
да даже стандартных, то компилятор обижается:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A { };
 
template <std::string s_value>
class B: public A {
  public:
    inline static const std::string s = s_value;
};
 
class X: public B<std::string("X")> { };
class Y: public B<std::string("Y")> { };
 
int main() {
    std::cout << X().s + Y().s;
}
Уверен, что не только я сталкиваюсь с этой проблемой.
Как лучше всего поступить, если я всё-таки не хочу дублировать
декларации статических данных в каждом наследнике?
0

Помощь в написании контрольных, курсовых и дипломных работ здесь.

Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
16.08.2018, 03:09
Ответы с готовыми решениями:

Как группировать столбцы правильно?
Люди, здравствуйте. Как группировать столбцы (да и строки). Вот, мне надо сделать в диапазоне...

Как правильно группировать и разбить такую строку?
как правильно группировать и развить такую строку. пример как может выглядеть строка....

Как правильно инициализировать статические члены класса?
Я новичок и заранее прошу прощение за тупые вопросы. Имеются два класса: Solution и Pool. Pool...

Как правильно инициализировать статические поля класса
Здравствуйте, уважаемые форумчане! Столкнулся с проблемой, решение нашел, но идею еще нужно...

11
15122 / 8120 / 1961
Регистрация: 30.01.2014
Сообщений: 13,804
16.08.2018, 10:54 2
Лучший ответ Сообщение было отмечено Lyosha12 как решение

Решение

Цитата Сообщение от Lyosha12 Посмотреть сообщение
компилятор обижается
Тебе обязательно нужно разобраться почему он "обижается".


А пока что можно присыпать сахарком.
Кликните здесь для просмотра всего текста
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
class A { };
 
template <typename T, T (* Init)()>
class B: public A {
public:
    inline static const T i = Init();
};
 
#define DEF_CLASS(C, Type, Data) \
    inline Type C##_init() { return (Data); } \
    class C : public B<Type, C##_init>
 
DEF_CLASS(X, char, 'r')
{
};
DEF_CLASS(Y, char, 'g')
{
};
DEF_CLASS(X1, std::string, "X")
{
};
DEF_CLASS(Y1, std::string, "Y")
{
};
 
int main() {
    std::cout << X().i + Y().i << std::endl;
    
    std::cout << X1().i + Y1().i << std::endl;
}


Но лучше рассказать нормально зачем это понадобилось, т.к. высока вероятность,
что ты просто борешься с ветряной мельницей.
1
Эксперт С++
1597 / 929 / 777
Регистрация: 06.02.2016
Сообщений: 2,414
Записей в блоге: 29
16.08.2018, 10:54 3
Note that nontype template parameters carry some restrictions. In general, they can
be only constant integral values (including enumerations), pointers to
objects/functions/members, lvalue references to objects or functions, or
std::nullptr_t (the type of nullptr).
Floating-point numbers and class-type objects are not allowed as nontype template
parameters:
Соответственно так нельзя
C++
1
template <std::string s_value>
0
36 / 37 / 10
Регистрация: 02.04.2016
Сообщений: 300
16.08.2018, 14:05  [ТС] 4
DrOffset, интересное решение. Это мне нужно вот для чего.

Есть двумерное игровое поле. Клетка игрового поля знает в какой позиции относительно других клеток она находится (её координата), а также имеет заполнитель (Filler).
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Cell: public sf::Drawable {
    // Представляет клетку на поле.
    // Клетка содержит заполнитель (filler).
    // Filler - это еда, препятствие, блок змейки и т.д.
    // Какой заполнитель установить и как решает бассейн клеток (CellsPool).
  
  public:
    using FillerUPtr = Filler::FUP;
    using CellCPtr   = Cell const*;
    using CellPtr    = Cell*;
    
  public:
    Cell() = default;
    Cell(Cell&& cell);
    Cell(Cell const&) = delete;
    
    Coord coord = {-1, -1};
    std::unique_ptr<Filler> filler = nullptr;
    
  private:
    void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
};


Чтобы игрок знал какое воздействие клетка на него окажет, Filler (базовый класс) хранит спрайт и функции, позволяющие менять игрока, когда тот получит клетку. Спрайт создаётся из текстуры, которая известна только наследнику Filler - уже конкретному заполнителю. В свою очередь, каждый конкретный заполнитель связывает себя с бонусом, который является паттерном "Command", согласно книге "Шаблоны игрового программирования" Р. Найстрома.
https://github.com/Lyosha12/Sn... ell/Filler
https://github.com/Lyosha12/Sn... ll/Fillers
https://github.com/Lyosha12/Sn... nusManager

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

Хранение текстуры статически очевидно: зачем каждый раз подгружать её с диска, когда хочу создать её?
Выходит, что каждый конкретный заполнитель должен хранить статически как текстуру, так и её направление. А если я ещё что-то добавлю? Опять писать кучу деклараций, вместо небольшой кучки инициализаций.
0
303 / 215 / 74
Регистрация: 23.05.2011
Сообщений: 971
16.08.2018, 14:36 5
А почему нельзя хранить текстуры и параметры в полях объекта?
0
36 / 37 / 10
Регистрация: 02.04.2016
Сообщений: 300
16.08.2018, 14:44  [ТС] 6
New man, объекта какого? Каждый конкретный заполнитель - это одна текстура. Уточни пожалуйста. По сути, я создал таким образом синглтон без ленивой инициализации - да она и не нужна, ведь кому интересно ждать, пока текстура подгрузится во время игры?
0
303 / 215 / 74
Регистрация: 23.05.2011
Сообщений: 971
16.08.2018, 14:48 7
Лучший ответ Сообщение было отмечено Lyosha12 как решение

Решение

Обычно, всё же, не делают отдельный класс под каждую текстуру.

Я бы сделал тут один класс с тремя полями: 1) битмар, 2) параметры, 3) указатель на бонус.
Инициализировал бы всё это перед игрой в каком-нибудь менеджере текстур, по одному объекту заполнителя на каждый нынешний твой класс, а к каждой клетке добавил бы указатель на соответствующий заполнитель.
1
36 / 37 / 10
Регистрация: 02.04.2016
Сообщений: 300
16.08.2018, 15:04  [ТС] 8
New man, это описание похоже на tailmap, тоже описанный приём в той книге. Надо подумать над этим, ещё раз. Учитывая, какую гору зависимостей я уже написал, кажется, что макросы усугубят вид кода. Я согласен с тобой. Значит, как закончу отладку, подумаю над разрешением этого бардака.

Для меня всегда вопросом остаётся интерфейс: насколько он хорош?
Если делать эдакую tailmap, то какое сообщение и как отправлять бассейну клеток, чтобы тот заменил заполнитель?
Будет ли это enum, в соответствие которому ставится индекс заполнителя в массиве (или map)?

Может, прямо указатель на заполнитель передать? Всё-таки вызывающий знает о заполнителе, который хочет создать.
Но тогда нужно делать существующие заполнители глобальными объектами - чтобы каждый имел к ним доступ.
Или обойтись помещением их в бассейн клеток, предоставив к ним какой-то интерфейс, но это переделанный первый вариант.
0
303 / 215 / 74
Регистрация: 23.05.2011
Сообщений: 971
16.08.2018, 15:33 9
Вариант с ID хорошо при для сохранений игр.

Делаешь статический класс ID-генератор вроде такого:

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
// header
class IDGen
{
   static void init_id(size_t initial_id); // Вызывается только при загрузке игры
   static size_t current_next_id_to_save(); // Вызывается при сохранении
   static size_t get_next_id(); // вызывается при создании любого нового объекта
};
 
// cpp
 
size_t next_id = 0;
 
void IDGen::init_id(size_t initial_id)
{
   next_id = initial_id;
}
 
size_t IDGen::current_next_id_to_save()
{
   return next_id;
}
 
size_t IDGen::get_next_id()
{
   return next_id++;
}
Потом в сейве соответственно:

Код
current_next_id = "твоё значение"

#############
описания объектов, где они ссылаются друг на друга по id, плюс прочие поля
Если же с сейвами возиться не надо, то проще указатели посылать, наверно.
0
36 / 37 / 10
Регистрация: 02.04.2016
Сообщений: 300
17.08.2018, 01:21  [ТС] 10
New man, вот, вроде, хорошая организация: каждая клетка на поле хранит enum-id,
который ссылается уже на внутренний элемент массива заполнителей в бассейне клеток.
Так, я избавляюсь от бесконечных обращений в кучу при создании заполнителя.
К тому же, появляется возможность мониторить папку с текстурами и подменять их во время работы игры,
а не только на старте.

Насчёт твоего IDGen. Я не совсем понял как использовать этот "типа счётчик" объектов.
Когда поле будет сохраняться, я просто сброшу матрицу из id заполнителей,
которые по совместительству служат индексами в массиве заполнителей.

Добавлено через 2 часа 7 минут
Правильно ли я понимаю, что твоё предложение с IDGen в том, чтобы было возможно создавать любой заполнитель в любой момент в игре? Если да, то на данный момент механика такой сложности мне пока не нужна.

Добавлено через 7 часов 12 минут
DrOffset, лямбда вписалась бы лучше, чем макросы. Наверное.
0
303 / 215 / 74
Регистрация: 23.05.2011
Сообщений: 971
17.08.2018, 08:08 11
Цитата Сообщение от Lyosha12 Посмотреть сообщение
Правильно ли я понимаю, что твоё предложение с IDGen в том, чтобы было возможно создавать любой заполнитель в любой момент в игре? Если да, то на данный момент механика такой сложности мне пока не нужна.
Ага. + Мы можем это сохранить в файл, загрузить из него, и все связи останутся.
0
15122 / 8120 / 1961
Регистрация: 30.01.2014
Сообщений: 13,804
17.08.2018, 08:22 12
Цитата Сообщение от Lyosha12 Посмотреть сообщение
лямбда вписалась бы лучше, чем макросы.
Лямбду нельзя использовать в параметрах шаблона.
Так бы конечно лучше вписалось.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
17.08.2018, 08:22

Как правильно объявлять и использовать статические переменные в Lazarus
Эта тема уже обсуждалась применительно к Delphi...

Как правильно объявлять статические константные объекты в классе?
В классе надо объявить статичные константные строки. Типа class A { public: static const...

Как записать в массив данные из *.TXT и и правильно рассортировать данные по ячейкам массива?
файл index.php &lt;?php $site = (file('text.txt')); в файле text.txt находится: xxxxxx.ru...

Статические данные класса
Здравствуйте. Подскажите, почему при создании класса Date не происходит бесконечного вызова...


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

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

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