Skip to main content

Command Palette

Search for a command to run...

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

Updated
3 min read
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 class

  • Range-based loops

  • RAII principles

17 views