- Use
std::vector
if the size is not extremely large.
Pointers always have a cost associated with them because of the indirection involved. Looking up an address and accessing it in memory may not be able to be optimized by the compiler out and will thus involve costs with accessing memory. Memory access is often the bottleneck for performance in systems, so it's best to try to put things near to each other in memory and try to structure your programs so that you access memory the least.
- Use a database system, like SQL, if the data gets extremely large.
On the other hand, we can forego all of the dirty work and use an established database library or program. Something like MySQL can easily manage a lot of data with a great programming language to access and manage it as well. Certain databases, like PostgreSQL can scale to large sets of data. Getting familiar with it can also be quite helpful. Even some mobile apps might use MySQL for Android, for example.
- Use the modern C++11 or greater
for
loop iteration syntax.
The current for
loop syntax is quite opaque and might have a lot of cruft. C++11 introduced a cleaner for
loop syntax to iterate across standard library containers like map
or vector
. Use: for(auto it : vector_name)
. If you need to modify each one, use a reference qualifier for the it
--the iterator.
- Use pre-increment syntax for possibly minimal speedup.
++i
and i++
are slightly different. ++i
just directly modifies the object where it appears in an expression before it continues evaluating it. i++
creates a copy of the object, returns it, and increments the original. Creating a copy of a value or object has a cost in C++, so avoiding this can be helpful in certain cases and it is a good convention to do this anyways.
- Pass by
const &
. Not by just regular reference.
Function arguments are passed by value by default in C++. This means that C++ just makes a copy of the object. However, when there are mutations applied repeatedly to an object, like, say, using a function to change the value of an integer over time, you may want to pass by reference. References basically pass the "real" object, meaning that any changes you make to the reference are done on the "real" object.
Now, why pass a non-modifiable object? Because it can lead to better optimizations. Passing by constant reference allows the compiler to make stronger assumptions about your code (e.g. because the reference cannot change within the course of the program, referring to the same reference multiple times in the function doesn't require the value of the argument to be reloaded over again because it shouldn't change while inside of a function).
- Use a
std::unique_ptr
or std::shared_ptr
.
Smart pointers are also a nice feature that was introduced with C++11, and involves pointers that automatically deallocate themselves by attaching their lifetime to scope. In other words, no need to use new
or delete
--just create the pointers and you shouldn't have to keep track of when to release memory. This can get complicated in certain situations but in general, using smart pointers leads to better safety and less change of having memory management problems, which is why they were inducted into the standard library in the first place.