Here is the variable std :: string text; Required in 3 classes descendants. along with the void method set_text ();

struct GUI { POSITION position;// общий для всех //std::string текст; вынести сюда? //void set_text(const std::string & текст_){текст=текст_;} }; struct TEXT : public GUI { std::string текст; void set_text(const std::string & текст_){текст=текст_;} } struct BUTTON : public GUI { std::string текст; void set_text(const std::string & текст_){текст=текст_;} } struct IMAGE : public GUI { } struct INPUT : public GUI { std::string текст; void set_text(const std::string & текст_){текст=текст_;} } 

It's easier for me to put it in a struct GUI so that there is one access point. But then IMAGE will store the extra string.

While I see several options:

  1. If in subclasses of variables it is required more than it is not necessary to bring it to the parent class.

  2. Create an additional parent class and inherit from it to those classes that require string text. If there are several common variables for a number of classes, apply multiple inheritance.

  3. Store in the parent class only what is common to all otherwise prescribe for each element.

In the present code, only 15 elements and the text is required in 9. The total of variables and structures in the GUI is 115 (exactly) and the memory for 100 different GUI instances eats under 400 mb. Therefore, it is necessary now to think things through and correctly design.



Update:

Wow .. I will try to show the main ridge. Each GUI has 2 required update () methods. Draw ()

that is, one update () function updates the state of all variables. another draw () passes the data to the draw. Draw cannot and does not have the right to change anything in the GUI, it simply calls the methods of the low-level drawing libraries.

update () just checks if there is a mouse over it, moves the slider, scrolls the page, etc. that is, modifies data members.

Now like this:

 struct GUI { TYPE_GUI type; POSITION position; IMAGE_LIB * image_фон; ... еще 100 переменных update(){ if(type==IMAGE){if(key_left == true){key_left =false; position.X += 10; image_фон->X = position.X;}} if(type==TEXT){..} ... } draw() { if(type==IMAGE){image_фон->render();} if(type==TEXT){..} ... } } std::map<string , GUI*> gui_elements; void game_thread() { gui_elements["map1"]->set_pos_X(unit_X); } void gui_thread() { for{ gui_elements->update(); } } void render_thread() { for{ gui_elements->draw(); } } 

game-thread - can write to an instance of gui-elements. other threads only read when there are updates to the state of the game-thread executed in the thread.

  • 2
    More logical option 2. - alexlz
  • This approach seems interesting to me, but it’s written in the wiki: “There is an opinion that multiple inheritance is an incorrect concept, generated by incorrect analysis and design.” Maybe there are other options? - manking
  • one
    There is also an opinion that inheritance should be avoided, giving preference to composition where possible. - avp
  • That is, as? Something like that? struct GUI {POSITION position; TEXT * text; BUTTON * button; IMAGE * image; or generally one void * el; ?? GUI (type) {text = 0; button = 0; image = 0; el = 0; if (type == TEXT) {text = new TEXT; -or- el = new TEXT;} if (type == BUTTON) {button = new BUTTON; -or- el = new BUTTON;} if (type == IMAGE) {image = new IMAGE; -or- el = new IMAGE;}}}; - manking
  • one
    update and draw really want to become virtual methods. - VladD

1 answer 1

I would try to replace inheritance with composition:

 // обратите внимание, тут нет никакого наследования class ImageHelper { IMAGE_LIB * image_фон; public: void update(Coordinates coord) { ... } void draw(Coordinates coord) { ... } }; class TextHelper { std::string text; public: void update(Coordinates coord) { ... } void draw(Coordinates coord) { ... } void setText(std::string newText) { text = std::move(newText); ... } }; class GuiObect { // TYPE_GUI больше не нужен public: virtual void update(); virtual void draw(); }; class TextControl : public GuiObject { // has one text TextHelper text; public: virtual void update() { // prepare coordinates text.update(coordinates); } virtual void draw() { // prepare coordinates text.draw(coordinates); // maybe draw borders etc. } }; class Button : public GuiObject { // has one text and maybe one bg image TextHelper text; ImageHelper* background; public: virtual void update() { // prepare coordinates if (background) background->update(coordinates); text.update(coordinates); } virtual void draw() { // prepare coordinates if (background) background->draw(coordinates); text.draw(coordinates); // maybe draw borders etc. } }; // etc. 

Thus, all the logic of working with a text line goes to TextHelper, and your controls simply contain one or more TextHelper instances, if necessary, and proxify setText calls, etc. to the desired TextHelper object.

Not sure if this is the best option, but maybe this is what you need.

  • I seem to understand the point. Is this some kind of design pattern called helper? I have not met him. And I can not find specific information on it. Do I understand correctly that the advantage of this approach in saving memory and performance? That is, when inheritance is forced initialization, even if the data is not needed? If the class Button: public GuiObject also inherited ImageHelper *, would it always be the construction of the ImageHelper along with all its data? Or is there something else that I don't see? - manking
  • 3
    @manking: Hm, I don't know if there is such a design pattern. I would just call it composition . The main advantage, for my taste, is in the separation of powers: all operations with text are transferred to a separate entity, which is essentially not a control. - VladD
  • one
    @VladD, it seems so. Only at the minimum coordinates in GuiObject probably need to be added (and it’s not clear where they come from in text.Draw () calls), well, there may be a link to parent (for organizing the hierarchy and containers). - avp
  • @avp: Yeah, you're right, I just sketched the skeleton. - VladD