All CoursesC++ Programming Course

Best Practices

Write clean, maintainable, and bug-free C++ code.

๐Ÿงน Clean Code Rules

One function, one job

Each function should do exactly one thing. If you can't describe it without 'and', split it.

Keep functions short

Aim for 20-30 lines max. If a function is longer, break it into helper functions.

Use const wherever possible

Mark variables, parameters, and methods as const when they shouldn't change.

Initialize all variables

Never rely on default values. Always give variables an explicit initial value.

Avoid magic numbers

Replace hardcoded numbers with named constants: const int MAX_STUDENTS = 100;

RAII โ€” Resource Acquisition Is Initialization

Acquire resources in constructors, release in destructors. Use smart pointers.

clean_code_example.cpp
// โŒ BAD โ€” Magic numbers, unclear names, too much in one function
void f(int a[], int n) {
    for(int i=0; i<n; i++)
        if(a[i] > 100) a[i] = 100;
    int s = 0;
    for(int i=0; i<n; i++) s += a[i];
    cout << s/n;
}

// โœ… GOOD โ€” Named constants, clear names, single responsibility
const int MAX_SCORE = 100;

void capScores(int scores[], int count) {
    for (int i = 0; i < count; i++) {
        if (scores[i] > MAX_SCORE) {
            scores[i] = MAX_SCORE;
        }
    }
}

double calculateAverage(const int scores[], int count) {
    int total = 0;
    for (int i = 0; i < count; i++) {
        total += scores[i];
    }
    return static_cast<double>(total) / count;
}

void displayClassAverage(int scores[], int count) {
    capScores(scores, count);
    double avg = calculateAverage(scores, count);
    cout << "Class average: " << avg << endl;
}

๐Ÿ“› Naming Conventions

naming_conventions.cpp
// Variables โ€” camelCase
int studentCount = 0;
double averageScore = 85.5;
string firstName = "Alice";

// Constants โ€” UPPER_SNAKE_CASE
const int MAX_RETRIES = 3;
const double TAX_RATE = 0.08;

// Functions โ€” camelCase (verb-first)
void calculateTotal();
bool isValidEmail(string email);
int findMaxValue(int arr[], int size);

// Classes โ€” PascalCase
class StudentManager { };
class BankAccount { };

// Private members โ€” with underscore or m_ prefix
class Employee {
    string m_name;       // m_ prefix style
    double salary_;      // trailing underscore style
};

// Enums โ€” PascalCase type, UPPER values
enum class Color { RED, GREEN, BLUE };
enum class Status { ACTIVE, INACTIVE, PENDING };

// File names โ€” snake_case
// student_manager.cpp
// bank_account.h

๐Ÿ”ง Debugging Techniques

1. Print Debugging

Add cout statements to trace variable values and execution flow. Remove them after fixing.

2. Rubber Duck Debugging

Explain your code line by line to an imaginary listener. The act of explaining often reveals the bug.

3. Binary Search Debugging

Comment out half your code. If the bug disappears, it's in the commented half. Repeat.

4. Read the Error Message

Compiler errors tell you the file, line, and what's wrong. Read from the first error โ€” later errors are often cascading.

5. Check Edge Cases

Test with: empty input, one element, very large values, negative numbers, and boundary values.

6. Use a Debugger

Learn GDB or your IDE's debugger. Set breakpoints, step through code, and inspect variables at runtime.

debugging_example.cpp
// Using print debugging to trace a sorting bug
void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        cout << "[DEBUG] Pass " << i + 1 << endl;  // Track passes
        
        for (int j = 0; j < n - i - 1; j++) {
            cout << "  Comparing arr[" << j << "]=" << arr[j]
                 << " with arr[" << j+1 << "]=" << arr[j+1] << endl;
            
            if (arr[j] > arr[j + 1]) {
                swap(arr[j], arr[j + 1]);
                cout << "  โ†’ Swapped!" << endl;
            }
        }
        
        // Print array state after each pass
        cout << "  Array: ";
        for (int k = 0; k < n; k++) cout << arr[k] << " ";
        cout << endl;
    }
}

โš ๏ธ Common Mistakes

common_mistakes.cpp
// โŒ MISTAKE 1: Using = instead of == in conditions
if (x = 5) { }    // WRONG โ€” assigns 5 to x (always true)
if (x == 5) { }   // CORRECT โ€” compares x to 5

// โŒ MISTAKE 2: Off-by-one errors in loops
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i <= 5; i++) { }  // WRONG โ€” accesses arr[5] (out of bounds!)
for (int i = 0; i < 5; i++) { }   // CORRECT โ€” stops at arr[4]

// โŒ MISTAKE 3: Forgetting to close files
ofstream file("data.txt");
file << "Hello";
// WRONG โ€” file not closed, data may not be written!
file.close();  // CORRECT โ€” always close your files

// โŒ MISTAKE 4: Integer division when you want decimal
int a = 7, b = 2;
double result = a / b;          // WRONG โ€” result is 3.0 (integer division)
double result = (double)a / b;  // CORRECT โ€” result is 3.5

// โŒ MISTAKE 5: Memory leaks with new/delete
int* p = new int(42);
p = new int(100);    // WRONG โ€” lost pointer to first allocation!
delete p;            // Only deletes the second one

// โŒ MISTAKE 6: Forgetting break in switch
switch (x) {
    case 1: cout << "One";    // WRONG โ€” falls through to case 2!
    case 2: cout << "Two";
}
switch (x) {
    case 1: cout << "One"; break;  // CORRECT
    case 2: cout << "Two"; break;
}

// โŒ MISTAKE 7: Dangling else
if (a > 0)
    if (b > 0)
        cout << "Both positive";
else                  // This else belongs to inner if, not outer!
    cout << "a is not positive";  // MISLEADING
// CORRECT: Always use braces
if (a > 0) {
    if (b > 0) {
        cout << "Both positive";
    }
} else {
    cout << "a is not positive";
}