Страницы

Поиск по вопросам

вторник, 10 декабря 2019 г.

Класс должен порождать дружественные классы

#cpp #классы #наследование


Представим, что я создал класс "квадрат". От него породил очень много классов разноцветных
квадратов, например: жёлтый, красный, синий и т.д. Теперь я хочу сделать так, что бы
цветной квадрат мог порождать при определённых обстоятельствах другие цветные квадраты.
Например, жёлтый квадрат с вероятностью 30% порождает 2 красных квадрата и 1 зелёный,
с вероятностью 50% 1 синий и 1 жёлтый, и так далее. Каждый цвет имеет свой набор распадов
на другие цвета (хотя есть цвета, которые ничего не порождают после себя). Не знаю
как это осуществить правильно. Пробовал вот как:

class квадрат{

   ....

   ....

   protected:

   ....

   std::vector(std::vector(квадрат *)) Decay;

   ....

   public:

   virtual void Initialize();

   ....

};


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

class жёлтый : public квадрат{

   ...

   void Initialize();

};


И описываю эту функцию:

void жёлтый::Initialize() {

    ....

    ....

    //(30%)

    Decay[0][0] = new красный;

    Decay[0][1] = new красный;

    Decay[0][2] = new зелёный;



    //(50%)

    Decay[1][0] = new синий;

    Decay[1][1] = new жёлтый;



    и т.д.

}


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

Извиняюсь, если что, я на форуме первый раз. Для меня форматирование текста осталось
загадкой.
    


Ответы

Ответ 1



Я бы предложил вот что. Вы не храните квадраты в квадрате, а лишь функции, которые могут их создать. Эти функции, понятно, места не занимают. У вас же каждый квадрат хранит в себе другие квадраты, которые в свою очередь хранят другие квадраты. Для каждого из них вызывается функция Initialize(), создающая новые квадраты, и т. д. Поэтому избавимся от хранения квадратов, и будем держать лишь функции, которые эти квадраты создают. Объявим их: class square; // предварительное объявление typedef std::function square_creator; Теперь сам класс квадрата. Нам нужна таблица распада, с создающими функциями и их вероятностями. Поскольку у каждого из подклассов таблица своя, функцию объявим абстрактной и виртуальной. На основе функции можно выполнять распад, его код общий для всех подклассов: class square { protected: virtual std::vector>& get_decay_table() = 0; public: square* decay(); }; Теперь конкретные подклассы. Здесь нужно объявить таблицу распада, и возвращать её в виртуальной функции: class yellow : public square { static std::vector> decay_table; protected: virtual std::vector>& get_decay_table() { return decay_table; } }; Другой класс будет выглядеть точно так же: class red : public square { static std::vector> decay_table; protected: virtual std::vector>& get_decay_table() { return decay_table; }; }; Ну и отдельно объявляем статические таблицы распада: std::vector> yellow::decay_table { { []() { return new yellow(); }, 0.3 }, { []() { return new red(); }, 0.7 }, }; std::vector> red::decay_table { { []() { return new yellow(); }, 0.6 }, { []() { return new red(); }, 0.4 }, }; Для реализации decay нам необходим генератор случайных чисел. Чтобы не было повторений, сделаем его общим. Для этого добавим в square private: static std::random_device rd; static std::mt19937 gen; static std::uniform_real_distribution<> dis; и в .cpp инициализацию: std::random_device square::rd; std::mt19937 square::gen(square::rd()); std::uniform_real_distribution<> square::dis(0, 1); Теперь можно реализовать функцию decay: square* square::decay() { auto& table = get_decay_table(); double random = dis(gen); for (auto& entry : table) { auto probability = entry.second; if (random < probability) return entry.first(); random -= probability; } // shouldn't happen return nullptr; } Для того, чтобы как-то отличать наши квадраты, добавим виртуальную функцию с именем в них. Теперь можно запускать тест: int main(int argc, char *argv[]) { square* s = new yellow(); for (int i = 0; i < 10; i++) { s = s->decay(); // здесь утечка памяти, не забудьте уничтожить старый квадрат! std::cout << s->name() << std::endl; } } Результат пробного запуска: red yellow red red yellow red yellow red red yellow На всякий случай, полный код: // эта строчка только для Visual Studio #include "stdafx.h" #include #include #include #include #include class square; typedef std::function square_creator; class square { protected: virtual std::vector>& get_decay_table() = 0; private: static std::random_device rd; static std::mt19937 gen; static std::uniform_real_distribution<> dis; public: square* decay(); virtual std::string name() = 0; }; std::random_device square::rd; std::mt19937 square::gen(square::rd()); std::uniform_real_distribution<> square::dis(0, 1); square* square::decay() { auto& table = get_decay_table(); double random = dis(gen); for (auto& entry : table) { auto probability = entry.second; if (random < probability) return entry.first(); random -= probability; } // shouldn't happen return nullptr; } class yellow : public square { static std::vector> decay_table; protected: virtual std::vector>& get_decay_table() { return decay_table; }; public: virtual std::string name() { return "yellow"; }; }; class red : public square { static std::vector> decay_table; protected: virtual std::vector>& get_decay_table() { return decay_table; }; public: virtual std::string name() { return "red"; }; }; std::vector> yellow::decay_table { { []() { return new yellow(); }, 0.3 }, { []() { return new red(); }, 0.7 }, }; std::vector> red::decay_table { { []() { return new yellow(); }, 0.6 }, { []() { return new red(); }, 0.4 }, }; int main(int argc, char *argv[]) { square* s = new yellow(); for (int i = 0; i < 10; i++) { s = s->decay(); // здесь утечка памяти, не забудьте уничтожить старый квадрат! std::cout << s->name() << std::endl; } } P.S.: Если у вас квадрат распадается на несколько квадратов, то вам нужно возвращать в decay std::vector, ну и соответственно в таблице держать функции создания нескольких квадратов для каждой из вероятностей.

Комментариев нет:

Отправить комментарий