I would suggest this.
You do not store squares in a square, but only functions that can create them. These functions, of course, do not take up space. In your case, each square stores other squares, which in turn store other squares. For each of them, the Initialize() function is called, which creates new squares, and so on. Therefore, we’ll get rid of storing the squares and keep only the functions that these squares create.
Declare them:
class square; // предварительное объявление typedef std::function<square*()> square_creator;
Now the class itself is a square. We need a decay table, with creating functions and their probabilities. Since each of the subclasses has its own table, we declare the function abstract and virtual. Based on the function, you can perform the decay, its code is common to all subclasses:
class square { protected: virtual std::vector<std::pair<square_creator, double>>& get_decay_table() = 0; public: square* decay(); };
Now specific subclasses. Here you need to declare a decay table, and return it in a virtual function:
class yellow : public square { static std::vector<std::pair<square_creator, double>> decay_table; protected: virtual std::vector<std::pair<square_creator, double>>& get_decay_table() { return decay_table; } };
The other class will look exactly the same:
class red : public square { static std::vector<std::pair<square_creator, double>> decay_table; protected: virtual std::vector<std::pair<square_creator, double>>& get_decay_table() { return decay_table; }; };
Well, separately we declare static decay tables:
std::vector<std::pair<square_creator, double>> yellow::decay_table { { []() { return new yellow(); }, 0.3 }, { []() { return new red(); }, 0.7 }, }; std::vector<std::pair<square_creator, double>> red::decay_table { { []() { return new yellow(); }, 0.6 }, { []() { return new red(); }, 0.4 }, };
To implement decay we need a random number generator. To avoid repetitions, we will make it general. To do this, add to square
private: static std::random_device rd; static std::mt19937 gen; static std::uniform_real_distribution<> dis;
and in .cpp initialization:
std::random_device square::rd; std::mt19937 square::gen(square::rd()); std::uniform_real_distribution<> square::dis(0, 1);
Now you can implement the decay function:
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; }
In order to somehow distinguish our squares, we add a virtual function with the name in them. Now you can run the test:
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; } }
Test run result:
red yellow red red yellow red yellow red red yellow
Just in case, the full code:
// эта строчка только для Visual Studio #include "stdafx.h" #include <iostream> #include <string> #include <vector> #include <functional> #include <random> class square; typedef std::function<square*()> square_creator; class square { protected: virtual std::vector<std::pair<square_creator, double>>& 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<std::pair<square_creator, double>> decay_table; protected: virtual std::vector<std::pair<square_creator, double>>& get_decay_table() { return decay_table; }; public: virtual std::string name() { return "yellow"; }; }; class red : public square { static std::vector<std::pair<square_creator, double>> decay_table; protected: virtual std::vector<std::pair<square_creator, double>>& get_decay_table() { return decay_table; }; public: virtual std::string name() { return "red"; }; }; std::vector<std::pair<square_creator, double>> yellow::decay_table { { []() { return new yellow(); }, 0.3 }, { []() { return new red(); }, 0.7 }, }; std::vector<std::pair<square_creator, double>> 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; } }
PS: If your square falls into several squares, then you need to return std::vector<square*> in decay , well, respectively, in the table to keep the function of creating several squares for each of the probabilities.