Skip to content

Best Practices and Coding Standards — C++ Core Guidelines, Naming Conventions, Code Review, Modern C++ Style

DodaTech Updated 2026-06-28 9 min read

In this tutorial, you will learn about Best Practices and Coding Standards. We cover key concepts, practical examples, and best practices to help you master this topic.

C++ best practices — guided by the C++ Core Guidelines and Bjarne Stroustrup's philosophy — emphasize RAII, value semantics, const correctness, smart pointers, and clear conventions for writing safe, readable, and maintainable code.

What You'll Learn

You will follow the C++ Core Guidelines for safer code, apply naming conventions (PascalCase for types, snake_case for functions/variables), use const correctness and noexcept where appropriate, write modern C++ using RAII, smart pointers, and algorithms, avoid common pitfalls (raw new/delete, C-style casts, macros), conduct effective code reviews, and use clang-tidy and sanitizers for automated enforcement.

Why It Matters

C++ gives you immense power and zero safety nets — a single uninitialized pointer can crash the program. Coding standards replace individual intuition with collective wisdom, preventing entire classes of bugs. C++ codebases that follow standards have fewer crashes, faster onboarding, and lower maintenance costs.

Learning Path

graph LR
    A["68: Performance Profiling"] --> B["69: Best Practices"]
    B --> C["70: Final Capstone Project"]
    C --> D["Done!"]
    style A fill:#4a90d9,stroke:#2c5f8a,color:#fff
    style B fill:#4a90d9,stroke:#2c5f8a,color:#fff
    style C fill:#4a90d9,stroke:#2c5f8a,color:#fff
    style D fill:#4a90d9,stroke:#2c5f8a,color:#fff

The C++ Core Guidelines

The Core Guidelines (isocpp.github.io/CppCoreGuidelines) are the definitive reference. Key rules:

Rule                    | Summary
------------------------|--------
P.1: Express ideas      | Code should reflect intent directly
P.5: Prefer compile-time| Catch errors at compile time when possible
P.8: Don't leak         | Use RAII for all resources
I.1: Interfaces         | Make interfaces explicit
I.30: Encapsulate       | Minimize exposure of internal details
F.15: Pass by ref       | Prefer const& for input parameters
F.21: Return multiple   | Return struct or tuple instead of out-params
C.1: Organize           | Class organizes related data + behavior
C.20: Default operations| Follow Rule of Zero or Rule of Five
C.41: Constructor       | Constructors should create fully initialized objects
C.80: RAII              | Use RAII wrappers for resource management
ES.1: Prefer the STL    | Use standard library instead of hand-rolled code
ES.20: Always initialize| Initialize all variables
CP.1: Assume data races| Assume code is multi-threaded; protect shared data

Naming Conventions

Consistent naming improves readability dramatically.

// Types: PascalCase
class BankAccount {};
struct Point2D {};
enum class Color {};
using StringList = std::vector<std::string>;
template <typename ValueType>
class Container {};

// Functions, variables, members: snake_case
int get_account_balance() const;
double calculate_total(const std::vector<double>& prices);
std::string user_name;           // local variable
int file_descriptor_;            // class member (trailing _)
static constexpr int k_max_size = 1024;  // constants: k prefix

// Macros: UPPER_SNAKE_CASE (minimize use)
#define DEBUG_LOG(msg) std::cerr << msg << "\n"
#define MAX_BUFFER_SIZE 4096

// Template parameters: PascalCase or Capital letters
template <typename T, typename Allocator>
class Vector;

// Namespaces: lowercase
namespace networking { namespace http { class Request {}; }}
using namespace networking::http;

Const Correctness

Use const wherever a value should not be modified.

#include <iostream>
#include <vector>
#include <string>

// Const parameters
void print(const std::string& text, int times);  // text won't be modified

// Const member functions
class Person {
    std::string name_;
    int age_;
public:
    // Getter: const because it doesn't modify the object
    const std::string& name() const { return name_; }
    int age() const { return age_; }

    // Setter: not const
    void set_age(int age) { age_ = age; }
};

// Const iterators for read-only access
void print_all(const std::vector<int>& v) {
    for (const auto& elem : v) {
        std::cout << elem << " ";
    }
}

// Const pointers vs pointer to const
const int* ptr_to_const;  // Cannot modify what ptr points to
int* const const_ptr;     // Cannot modify ptr itself
const int* const const_ptr_to_const;  // Both

// Constexpr for compile-time values
constexpr double PI = 3.14159;
constexpr int factorial(int n) { return n <= 1 ? 1 : n * factorial(n - 1); }

Modern C++ vs Old C++

// 1. Smart pointers vs raw pointers
// OLD:
Object* ptr = new Object();
delete ptr;

// MODERN:
auto ptr = std::make_unique<Object>();     // Unique ownership
auto shared = std::make_shared<Object>();  // Shared ownership

// 2. Algorithms vs manual loops
// OLD:
std::vector<int> v = {3, 1, 4, 1, 5};
std::vector<int> evens;
for (size_t i = 0; i < v.size(); ++i) {
    if (v[i] % 2 == 0) evens.push_back(v[i]);
}

// MODERN:
auto evens = v | std::views::filter([](int x) { return x % 2 == 0; });

// 3. nullptr vs NULL/0
// OLD: int* ptr = NULL;
// MODERN:
int* ptr = nullptr;

// 4. Range-for vs index
// OLD:
for (int i = 0; i < v.size(); ++i) std::cout << v[i];
// MODERN:
for (const auto& elem : v) std::cout << elem;

// 5. Override keyword
class Base { virtual void func() {}; };
class Derived : public Base {
    // OLD: void func();
    // MODERN:
    void func() override;  // Compiler checks it overrides
};

// 6. enum class vs plain enum
// OLD:
enum Color { RED, GREEN, BLUE };  // Pollutes namespace
// MODERN:
enum class Color { Red, Green, Blue };  // Scoped

// 7. auto for type deduction
// OLD:
std::vector<int>::iterator it = v.begin();
// MODERN:
auto it = v.begin();

// 8. Lambda vs functor
// OLD: struct Adder { int operator()(int a, int b) { return a + b; } };
// MODERN:
auto adder = [](int a, int b) { return a + b; };

What NOT to Do

// 1. No raw new/delete
void bad() {
    auto* p = new int(5);
    // ... if something throws: leak!
    delete p;
}

// 2. No C-style casts
double pi = 3.14;
// int x = (int)pi;           // BAD
int x = static_cast<int>(pi); // GOOD

// 3. No #define for constants
// #define MAX_SIZE 100       // BAD
constexpr int k_max_size = 100; // GOOD

// 4. No using namespace std in headers
using namespace std; // BAD in headers — pollutes all includers

// 5. No macros for functions
// #define SQUARE(x) x*x      // BAD (x+1)*(x+1)? No!
template <typename T>
constexpr T square(T x) { return x * x; } // GOOD

// 6. No trailing underscores for local variables
int count_; // BAD: looks like member variable
int count;  // GOOD

// 7. No global mutable state (unless absolutely necessary)
int global_counter = 0;  // BAD: thread-unsafe, unpredictable

// 8. No manual memory management
// std::vector<int>* vec = new std::vector<int>();  // BAD
std::vector<int> vec;  // GOOD

Code Review Checklist

A systematic checklist for reviewing C++ code:

[ ] Security: Any buffer overflows? Format string bugs? Integer overflow?
[ ] Lifetime: Any dangling pointers/references? Use-after-free?
[ ] Ownership: Who owns this resource? unique_ptr vs shared_ptr?
[ ] RAII: Are resources wrapped in RAII objects?
[ ] Exception safety: What happens if an exception is thrown?
[ ] Const correctness: Should parameters/methods be const?
[ ] noexcept: Should this function be noexcept?
[ ] Move semantics: Can we avoid copies?
[ ] Includes: Minimize, use forward declarations where possible
[ ] Naming: Does it follow project conventions?
[ ] Override: Are virtual overrides marked override?
[ ] Complexity: Can this be simplified?
[ ] Testing: Is there a unit test for this code?
[ ] Documentation: Is the intent clear?
[ ] Performance: Is there a faster/easier approach?

Automated Tools

# clang-tidy — static analysis
clang-tidy --checks='modernize-*,cppcoreguidelines-*' source.cpp

# clang-format — code formatting
clang-format -i --style=Google source.cpp

# AddressSanitizer (ASan) — memory errors
g++ -fsanitize=address -g -O1 -o program program.cpp

# UndefinedBehaviorSanitizer (UBSan)
g++ -fsanitize=undefined -g -O1 -o program program.cpp

# ThreadSanitizer (TSan) — data races
g++ -fsanitize=thread -g -O1 -o program -lpthread program.cpp

# Valgrind — comprehensive memory analysis
valgrind --leak-check=full ./program

# Include What You Use
iwyu source.cpp
// .clang-format — Google style
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 100
AllowShortFunctionsOnASingleLine: Inline

Common Mistake Patterns

Pattern Problem Fix
new/delete Leaks on exception unique_ptr / make_unique
#define CONST No type safety constexpr variable
(int)cast Hard to find, unsafe static_cast<int>
char buf[100] Buffer overflow std::string / std::array
if (p = nullptr) Assignment instead of == Compile with -Wall
throw in dtor terminate() if unwinding Never throw from dtor
virtual without override Silent overload slip Add override
unsigned for loop Infinite loop on decrement Use signed or size_t
std::bind Verbose, lambda is better Use lambda
this-> everywhere Noise Use only when needed (templates)

Practice Questions

  1. What is the most important C++ best practice? Answer: Use RAII for all resource management. It prevents leaks for memory, files, locks, and every other resource.

  2. Why should you avoid #define for constants and functions? Answer: Macros have no type safety, no scope, and can cause subtle bugs (double evaluation, precedence issues). Use constexpr instead.

  3. What does the override keyword do? Answer: It tells the compiler that the function is meant to override a virtual function. If it doesn't, the compiler produces an error.

  4. When should you use unique_ptr vs shared_ptr? Answer: unique_ptr for exclusive ownership (default). shared_ptr for truly shared ownership when the last owner's lifetime is unknown.

  5. Name three sanitizers and what they detect. Answer: AddressSanitizer (memory errors), UndefinedBehaviorSanitizer (UB), ThreadSanitizer (data races).

FAQ

What are the C++ Core Guidelines

The C++ Core Guidelines are a comprehensive set of rules for modern C++ development, authored by Bjarne Stroustrup and Herb Sutter. They cover safety, performance, and maintainability.

What is the Rule of Zero

If a class does not manage resources directly, define none of the special member functions. The compiler generates correct defaults. If the class manages resources, follow the Rule of Five.

Should I use exceptions or error codes

Use exceptions for exceptional situations (can't recover). Use std::optional or expected-like patterns for expected failures. Avoid exception specifications (except noexcept).

What is the best C++ style guide

Google C++ Style Guide and LLVM Coding Standards are popular. The Core Guidelines provide rationale. Choose one and apply it consistently.

How do I enforce coding standards in a team

Use clang-format for formatting, clang-tidy for static analysis, and pre-commit hooks. Code review every change with a checklist.

Mini Project

Review this buggy code against the C++ Core Guidelines and fix all violations:

// review_me.cpp — find and fix all guideline violations
#include <iostream>
#include <vector>
#include <string>

#define MAX_SIZE 100

class data {
public:
    char* buffer;
    int size;
    data(int s) { buffer = new char[s]; size = s; }
    ~data() { delete buffer; }
};

int* get_ptr() {
    int x = 42;
    return &x;
}

void process(data d) {
    for (int i = 0; i <= d.size; i++) {
        d.buffer[i] = 'a';
    }
}

int main() {
    data d(10);
    process(d);

    int* p = get_ptr();
    std::cout << *p << "\n";

    std::vector<int> v;
    for (int i = 0; i < 100; i++) v[i] = i;

    return 0;
}

Violations to find:

  1. Macro instead of constexpr
  2. PascalCase for class names
  3. Raw new/delete (RAII violation)
  4. Dangling pointer (returning address of local)
  5. Off-by-one and missing null terminator
  6. No copy constructor (double delete)
  7. Wrong delete (should be delete[])
  8. Vector out-of-bounds access
  9. Missing virtual destructor
  10. Pass by value instead of const ref

This exercise demonstrates how C++ best practices prevent real bugs. Compare with Java where memory management and dangling pointers are non-issues due to Garbage Collection.

What's Next

You now know C++ best practices and coding standards. Next, you will apply everything you've learned in the final capstone project — building a complete C++ application from scratch.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro