89 lines
2.8 KiB
Text
89 lines
2.8 KiB
Text
|
%% title = "C++ without Classes"
|
||
|
|
||
|
- one thing I often see in people's code in C++ is putting _everything_ into a class.
|
||
|
|
||
|
- there's nothing wrong with putting code that actually, y'know, _uses the class_, in the class, but it irks me when I see things like helper functions and other implementation details being in the class.
|
||
|
|
||
|
- private functions have the unfortunate consequence of being part of the public header, even though they are implementation details!
|
||
|
this means if you edit them, you have to edit both the .cpp implementation file, as well as the .h header.
|
||
|
|
||
|
- which is precisely what C++ without Classes is about.
|
||
|
since updating method signatures is a pain in the ass... just don't do it!
|
||
|
|
||
|
- declare all your fields public, and let `static` functions within your `.cpp` file work the magic.
|
||
|
|
||
|
- and here you might say: "but I don't _want_ to have these fields public! it breaks encapsulation!"
|
||
|
to which I respond: so make your data structure private!
|
||
|
|
||
|
- example of C++:
|
||
|
|
||
|
#### world.h
|
||
|
|
||
|
```cpp
|
||
|
struct World
|
||
|
{
|
||
|
// ... fields ...
|
||
|
|
||
|
void update(const Update_Context& context);
|
||
|
|
||
|
private:
|
||
|
void update_aims(const Update_Context& context);
|
||
|
};
|
||
|
```
|
||
|
|
||
|
#### world.cpp
|
||
|
|
||
|
```cpp
|
||
|
void World::update(const Update_Context& context)
|
||
|
{
|
||
|
update_aims(context);
|
||
|
}
|
||
|
|
||
|
void World::update_aims(const Update_Context& context)
|
||
|
{
|
||
|
// ...
|
||
|
}
|
||
|
```
|
||
|
|
||
|
- example of C++ without Classes:
|
||
|
|
||
|
#### world.h
|
||
|
|
||
|
```cpp
|
||
|
struct World
|
||
|
{
|
||
|
// ... fields ...
|
||
|
};
|
||
|
|
||
|
void update(World& world, const Update_Context& context);
|
||
|
```
|
||
|
|
||
|
#### world.cpp
|
||
|
|
||
|
```cpp
|
||
|
static void update_aims(World& world, const Update_Context& context)
|
||
|
{
|
||
|
// ...
|
||
|
}
|
||
|
|
||
|
void update(World& world, const Update_Context& context)
|
||
|
{
|
||
|
update_aims(world, context);
|
||
|
// ...
|
||
|
}
|
||
|
```
|
||
|
|
||
|
- suddenly, the publicly available part of `world.h` has gotten smaller---there's one method less.
|
||
|
the implementation detail `update_aims` does not leak into the header; we don't have to update the signature in two places if we ever want to change it, and it does not cause a recompilation if we edit it.
|
||
|
|
||
|
- I took the `update` method out of the struct to signal the lack of coupling.
|
||
|
_anyone_ can do what `update` does; it doesn't matter.
|
||
|
every function is at the same level as the struct itself.
|
||
|
|
||
|
- in my own code, outside this contrived example, I also put functions related to a data structure in a namespace---in this case, `update(World&, const Update_Context&)` would be `world::update(World&, const Update_Context&)`.
|
||
|
this is purely a stylistic preference---I like the extra explicitness over leaning on overloads.
|
||
|
|
||
|
- I've been enjoying this style a lot.
|
||
|
it takes edge cases like `this` out of the language, and results in simpler, more easily editable code.
|
||
|
so I wholeheartedly recommend giving it a try!
|