Do’s and Don’ts of C++: Writing Safer and More Reliable Code

C++ gives developers a lot of control over memory and performance. That control is also the reason many bugs in C++ are subtle, hard to detect, and sometimes disastrous.
Most of these issues are not caused by advanced features, but by small, everyday mistakes.
This blog covers practical do’s and don’ts in C++ that help you write safer, more modern, and more maintainable code.
Don’t Access Arrays Out of Bounds
Do Prefer Safer Containers Like std::vector
Problematic code using arrays
int arr[3] = {1, 2, 3};
cout << arr[5]; // Undefined behavior
This code compiles successfully. However, accessing an index outside the array bounds leads to undefined behavior. The program might crash, print garbage values, or appear to work correctly and fail later.
Safer approach using vectors
vector<int> v = {1, 2, 3};
cout << v.at(5); // Throws std::out_of_range
The at() method performs bounds checking and throws an exception when the index is invalid. This makes bugs visible instead of silently corrupting memory.
Don’t Use Raw Arrays by Default
Do Use std::vector or std::array
Raw arrays have fixed size and no built-in safety.
int arr[100];
Preferred alternatives:
vector<int> v(100); // Dynamic size
array<int, 100> a; // Fixed size but safer
These containers integrate well with the STL and reduce the chances of memory-related bugs.
Don’t Manage Memory Manually
Do Use Smart Pointers
Manual memory management is error-prone.
int* p = new int(10);
// ...
delete p;
Forgetting delete, deleting twice, or throwing an exception before cleanup can cause leaks or crashes.
Modern C++ approach:
auto p = make_unique<int>(10);
Smart pointers automatically manage object lifetimes and are safer in the presence of exceptions.
Don’t Pass Large Objects by Value
Do Pass by Reference or Const Reference
Passing containers by value causes unnecessary copying.
void process(vector<int> v); // Inefficient
Better approach:
void process(const vector<int>& v);
This avoids copying while also preventing accidental modification.
Don’t Use using namespace std; in Header Files
Do Use Qualified Names
using namespace std; // Avoid in headers
This can cause name collisions in large projects.
Preferred:
std::vector<int> v;
std::cout << "Hello";
Don’t Use C-Style Strings
Do Use std::string
C-style strings are a common source of buffer overflows.
char name[10];
strcpy(name, "VeryLongName"); // Unsafe
Safer alternative:
string name = "VeryLongName";
std::string manages memory automatically and grows as needed.
Don’t Modify a Container While Iterating Over It Incorrectly
Do Use Iterators Carefully
Incorrect:
for (int x : v) {
if (x == 0)
v.push_back(x);
}
Correct:
for (auto it = v.begin(); it != v.end(); ) {
if (*it == 0)
it = v.erase(it);
else
++it;
}
Modifying a container during iteration requires careful handling to avoid invalidated iterators.
Don’t Ignore const
Do Use const Wherever Possible
void print(const vector<int>& v);
Using const:
Prevents accidental modification
Improves readability
Helps the compiler optimize code
Don’t Write C++ Like C
Do Use Modern C++ Practices
Avoid:
Raw pointers
Manual memory management
C-style casts
Macros for constants
Prefer:
STL containers
Smart pointers
constexpr,enum classRange-based loops
RAII principles



