Imagine that I created a class "square". From it spawned a lot of classes of multi-colored squares, for example: yellow, red, blue, etc. Now I want to make it so that a colored square could generate, under certain circumstances, other colored squares. For example, a yellow square with a probability of 30% generates 2 red squares and 1 green, with a probability of 50% 1 blue and 1 yellow, and so on. Each color has its own set of decays into other colors (although there are colors that produce nothing after themselves). I do not know how to do it right. I tried this:

class квадрат{ .... .... protected: .... std::vector(std::vector(квадрат *)) Decay; .... public: virtual void Initialize(); .... }; 

Now I generate from it a new class and in it I define the function Initialize (), in which I describe which squares this data leaves behind, which I will simply call later in the constructors.

 class жёлтый : public квадрат{ ... void Initialize(); }; 

And I describe this function:

 void жёлтый::Initialize() { .... .... //(30%) Decay[0][0] = new красный; Decay[0][1] = new красный; Decay[0][2] = new зелёный; //(50%) Decay[1][0] = new синий; Decay[1][1] = new жёлтый; и т.д. } 

As I thought, when creating a yellow square, he creates everything inside himself that is described in the vector, including himself and memory overflow occurs. I wanted to create a simple vector in which the pointers to the objects would be stored, not the objects themselves, and then with a certain probability the object would generate others. Please tell me how to do it. In fact, my task is not with squares, but with the decay of elementary particles. Each particle with a certain probability can decay into a certain spectrum of particles, but the principle is similar.

I apologize, if anything, I'm on the forum for the first time. For me, text formatting remains a mystery.

  • one
    Your decay data does not refer to a specific yellow square, but to all yellow squares. It makes sense to declare these data static. - VladD
  • 2
    it seems there is no need to store the vector of objects in the class. We just need a function, for example, a decay that will return the desired vector with the necessary elements - Grundy
  • one
    otherwise it turns out that inside one square there are already squares :) but judging by the problem, this is not so - Grundy
  • one
    @Grundy: And recursively down to infinity! Here is the memory overflow. - VladD
  • Grundy, thanks, also thought about it. That is, you need a function in which I pass the address of a vector of type "square", which in the function is filled with new squares? - M'aiq the Liar

1 answer 1

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.

  • Wow, thank you.) Now we need to figure it all out) - M'aiq the Liar
  • but I didn’t understand what the meaning of the red / yellow classes is if they are the same? - Grundy
  • @Grundy: Well, consider that these are particles that someone falls into. They will have more static properties there, atomic mass there or something else. I added a name to be seen on the test run. - VladD
  • That is, they still have to be different? - Grundy
  • Well yes. The class has some other data that I do not provide (since I do not know). But the piece that I brought is the same. With the exception of the table, each has its own. - VladD