0
\$\begingroup\$

I'm a bit conflicted when it comes to the use of classes currently, I use them all the time, but I realized through the KISS and YAGNI principles I can simplify things by not using them, but is there any reason to not use a class? They seem to bring a lot to the table even if you don't utilize everything you at least have the ability to do so later.

For example, I made this class which is responsible for imgui stuff and calling registered callback functions, but it doesn't really benefit from being class at the moment.

class GUI {
public:
    GUI(SDL_Window* window, SDL_GLContext context);
    ~GUI(); 
    void Draw();
private:
    void start();
    void end();
};

So I could do something like this instead which I suppose is simpler.

namespace GUI {
    void init(SDL_Window* window, SDL_GLContext context);
    void shutdown();
    void start();
    void draw();
    void end();
}

But is this any better than just using a class in the first place? I feel like it just limits what I could do later down the road.

\$\endgroup\$
5
  • 2
    \$\begingroup\$ "but it doesn't really benefit from being class at the moment." oh is that so? This example shows a clear advantage of using classes: two members are private which means you get to change their signature in the future without impacting anyone who uses the class.. \$\endgroup\$
    – Vaillancourt
    Feb 21 at 22:58
  • 2
    \$\begingroup\$ This question seems to fit better at softwareengineering.stackexchange.com \$\endgroup\$
    – Theraot
    Feb 22 at 0:20
  • \$\begingroup\$ @Theraot why software engineering, am I missing something? \$\endgroup\$
    – Pow
    Feb 22 at 11:20
  • \$\begingroup\$ @Pow This is not how to solve some programming issue, so it is not Stackoverflow. It is not about game development or game design, so it is not gamedev. Instead it is about software design and development practices, so it is softwareengineering. Addenum: I am not saying this is not useful for game development, but there is nothing in the question that is game development specific. I also want to point out that just because a question is on topic on an stackexchange site, it does not mean it is not welcome on a different one (as long as it is not a cross post). I'm saying it fits better there. \$\endgroup\$
    – Theraot
    Feb 22 at 14:45
  • \$\begingroup\$ @Theraot I think you have perhaps misunderstood the question, hes using IMGUI, which is a library for drawing and creating UI for video games in C++. \$\endgroup\$
    – Pow
    Feb 22 at 18:40

3 Answers 3

2
\$\begingroup\$

Good question.

First what role do classes serve?

No seriously, why do we use classes? Some might say thtat it is to bundle related bits of data together... Such as a rectangle has a location, a width, and a height... But structs allow us to bundle related data... So why do we use them?

We use classes because of polymorphism, and encapsulation.

Polymorphism

Polymorphism allows us to treat apples, oranges, and strawberries as fruit. We can eat them, they all have weight, etc... Without having to perform checks outside of the orange/apple/strawberry to determine how they should be proccessed... All of the related information is self contained...

Your GUI? probably not going to need polymorphism there.

Encapsulation

Encapsulation is all about reducing cognitive load. By limiting what you/ future users can see.

Encapsulation is all about limiting how people can interact with data. Limited access makes interacting with a component easier to do in the way that was intended... And harder to do in unintended manners.

For example not allowing future users to call GUI::start/GUI::end or to touch any of private data that GUI needs to keep track of for more complicated functionality..

Getting into the specifics

I disagree with your assessment that your class as written doesn't benefit from being a class.

  1. Globals are bad, with globals x = use_globals(5, 1) might not have the same result as x = use_globals(5, 1) does.. despite having the same parameters... draw() your GUI should probably contain some private variables so that Draw() doesn't rely on globals.

  2. RAII is a wonderful concept... Whereas having a dependency between an init() and deinit() causes all sorts of problematic behavior. func OpenGUI(args...) { GUI my_gui(window, context); while(my_gui.not_canceled()) { my_gui.Draw(); if (something) { return some_value; } return something_else; } //my_gui automatically shutsdown no matter how I exit...

vs:

func OpenGUI(args...) {
    GUI::init(window, context);
    while(??) {
       draw(); // is draw relying on global variables to determine what it is drawing?
       if (something) {
          return some_value; //early exit
       }
    }
    shutdown();
    return something_else;
}
//Whoops I won't always call shutdown() like I should.
\$\endgroup\$
3
  • 1
    \$\begingroup\$ The GUI is a prime example of where you should use polymorphism. For example, deriving "health bar", "mana bar", and "fatigue bar" from a generic "progress bar" class gets you a whole lot of code re-use. \$\endgroup\$
    – Mark
    Feb 22 at 3:30
  • \$\begingroup\$ in C#, you cant put things directly in a namespace. Instead you're forced to use classes. However if you want a class that acts similarly to a namespace you can use a sealed class, which means you cant derive from it. \$\endgroup\$
    – Pow
    Feb 22 at 11:22
  • \$\begingroup\$ @Mark it depends on what "GUI" is being used for... I kinda assumed OPs GUI was the whole shebang... Not a single component. \$\endgroup\$
    – Questor
    Feb 22 at 18:00
2
\$\begingroup\$

The other answers here are great, but I wanted to add my two cents.

Classes in C++ offer quite a few conveniences, other answers here mentioned Encapsulation, Polymorphism but there's also Inheritance. Overall you can emulate all this functionality in C using a struct, but is it worth it?

The simple answer is, this is all there for convenience and to make the code more maintainable. When you have a small project, it might be easy to just use global variables for everything, not use a Class at all, and still make something that works.

The problem comes when you add features, and suddenly your code has hundreds of variables in scope, even though they are not relevant/needed. It suddenly becomes a problem of, can you keep working on this project? Or is it that complex that it feels too much effort?

From experience, I've seen more projects being abandoned for being unmaintainable, than from not being performant enough. Just because your project feels maintainable in its early steps, that doesn't mean it will remain that way as you work on it.

\$\endgroup\$
1
\$\begingroup\$

C++ is a huge language, and there can be reasons not to use particular feature, and lots of argument about which features should be used.

For instance, if you use polymorphism, you are paying a small speed penalty for the indirectness of virtual method calls, and a memory penalty (on 64 bit platforms, 8 bytes to store a pointer to the virtual method table). If your class is in a very tight loop or you have lots of objects, this could matter.

The only reason I can think of not to use encapsulation is quick prototyping, and that will mess you up as you scale.

The game I'm working on has no virtual methods at all (yet, that might change), but I use encapsulation extensively.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .