A fairly common thing people often forget is the following line:
srand(time(nullptr));
If you notice, the numbers being generated are exactly the same each time you launch the game-chances are that you haven't seeded the random number generator or you haven't provided a proper seed. It's recommended to always use a unix timestamp, as shown.
Tip
The use of this particular random function should be restricted to something that isn't related to security and cryptography. Using it in combination with the modulus operator can produce incredibly non-uniform results due to the introduced bias.
Another fairly common problem is the programmers' choice of the data container to hold their structures. Let's take the following for example:
using SnakeContainer = std::vector<SnakeSegment>;
This defines the type of our SnakeContainer. If you've compiled the code we've written, you will notice that it runs fairly smoothly. Now consider this next line of code:
Using SnakeContainer = std::deque<SnakeSegment>;
Because of the way these two containers are implemented in STL, nothing else changes in our code, so feel free to try to change the data type of your SnakeContainer from std::vector to std::deque. After compiling and running the project, you will definitely pick up on the hit on performance. Why is that happening? Well, even though std::vector and std::deque can be used basically in the same way, they're fundamentally different under the hood. The vector offers the certainty of its elements being contiguous in memory, while the double ended queue does not. There are also differences in performances, depending on where the most inserts and removals are done. If you're unsure about which container to use, make sure to either look it up or benchmark it yourself. Never just blindly assume, unless performance isn't the main concern to you.
Lastly, on a more open-ended note, don't be afraid to play with, modify, change, hack, or otherwise alter any piece of code that you see. The biggest mistake you can make is the mistake of not learning by breaking and fixing things. Consider the code we've written as only a push in the right direction and not a specific recipe. If understanding something better means you have to break it first, so be it.