1
Fork 0
treehouse/content/programming/cxx-without-classes.tree
2025-02-28 15:14:03 +01:00

100 lines
3.3 KiB
Text

%% title = "C++ without Classes"
% id = "01JN6EFWCPWY65AQ27BGFDT2DV"
- one thing I often see in people's code in C++ is putting _everything_ into a class.
% id = "01JN6EFWCPNBZA3P7NPTPJYFN5"
- 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.
% id = "01JN6EFWCPHYJNV6WDF1WJ3RSF"
- 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.
% id = "01JN6EFWCPHBEBPZXC34H65BMS"
- which is precisely what C++ without Classes is about.
since updating method signatures is a pain in the ass... just don't do it!
% id = "01JN6EFWCP8996EGW6P1AX203J"
- declare all your fields public, and let `static` functions within your `.cpp` file work the magic.
% id = "01JN6EFWCPPFW6N974T6GG0YZN"
- 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!
% id = "01JN6EFWCPEJ60RTS66314K7NN"
- 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)
{
// ...
}
```
% id = "01JN6EFWCP00TB3PGNVNP1T7S5"
- 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);
// ...
}
```
% id = "01JN6EFWCPER7ZSEA8NY0Z0JX2"
- 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.
% id = "01JN6EFWCP0XMH10NE6THK58EV"
- 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.
% id = "01JN6EFWCP3JV68Z1FR64V16JA"
- 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.
% id = "01JN6EFWCPNSQH38ED9ASC0CMX"
- 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!