Skip to content

Latest commit

 

History

History
375 lines (303 loc) · 12.2 KB

File metadata and controls

375 lines (303 loc) · 12.2 KB

C++ Best Practices

Security Principles

  • Prefer std::string over C-style strings to avoid manual memory management and buffer overflows.
  • Always validate input size and type when using standard input/output streams.

Memory Management

  • Smart Pointers:

    • Use std::unique_ptr for ownership semantics and std::shared_ptr for shared ownership.
    • Avoid raw pointers wherever possible.
  • Containers:

    • Use std::vector instead of dynamic arrays for automatic memory management.
    • Prefer std::map and std::unordered_map for associative data storage.

Exception Safety

  • Use exception handling (try-catch) for robust error management.
  • Always catch exceptions by reference:
    try {
        // code
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
    

C++ Coding Standards: Best Practices

This document highlights the best practices for developing maintainable and high-quality C++ code. It is derived from the 101 rules and guidelines provided in C++ Coding Standards: 101 Rules, Guidelines, and Best Practices by Herb Sutter and Andrei Alexandrescu.


Organizational and Policy Issues

  1. Don’t sweat the small stuff. (Know what not to standardize.)
  2. Compile cleanly at high warning levels.
  3. Use an automated build system.
  4. Use a version control system.
  5. Invest in code reviews.

Design Style

  1. Give one entity one cohesive responsibility.
  2. Correctness, simplicity, and clarity come first.
  3. Know when and how to code for scalability.
  4. Don’t optimize prematurely.
  5. Don’t pessimize prematurely.
  6. Minimize global and shared data.
  7. Hide information.
  8. Know when and how to code for concurrency.
  9. Ensure resources are owned by objects. Use explicit RAII and smart pointers.

Coding Style

  1. Prefer compile- and link-time errors to runtime errors.
  2. Use const proactively.
  3. Avoid macros.
  4. Avoid magic numbers.
  5. Declare variables as locally as possible.
  6. Always initialize variables.
  7. Avoid long functions. Avoid deep nesting.
  8. Avoid initialization dependencies across compilation units.
  9. Minimize definitional dependencies. Avoid cyclic dependencies.
  10. Make header files self-sufficient.
  11. Always write internal #include guards. Never write external #include guards.

Functions and Operators

  1. Take parameters appropriately by value, (smart) pointer, or reference.
  2. Preserve natural semantics for overloaded operators.
  3. Prefer the canonical forms of arithmetic and assignment operators.
  4. Prefer the canonical form of ++ and --. Prefer calling the prefix forms.
  5. Consider overloading to avoid implicit type conversions.
  6. Avoid overloading &&, ||, , (comma).
  7. Don’t write code that depends on the order of evaluation of function arguments.

Class Design and Inheritance

  1. Be clear about the kind of class you’re writing.
  2. Prefer minimal classes to monolithic classes.
  3. Prefer composition to inheritance.
  4. Avoid inheriting from classes that were not designed to be base classes.
  5. Prefer providing abstract interfaces.
  6. Public inheritance is substitutability. Inherit, not to reuse, but to be reused.
  7. Practice safe overriding.
  8. Consider making virtual functions nonpublic, and public functions nonvirtual.
  9. Avoid providing implicit conversions.
  10. Make data members private, except in behaviorless aggregates (C-style structs).
  11. Don’t give away your internals.
  12. Use the Pimpl idiom judiciously.
  13. Prefer writing nonmember nonfriend functions.
  14. Always provide new and delete together.
  15. If you provide any class-specific new, provide all of the standard forms (plain, in-place, and nothrow).

Construction, Destruction, and Copying

  1. Define and initialize member variables in the same order.
  2. Prefer initialization to assignment in constructors.
  3. Avoid calling virtual functions in constructors and destructors.
  4. Make base class destructors public and virtual, or protected and nonvirtual.
  5. Destructors, deallocation, and swap never fail.
  6. Copy and destroy consistently.
  7. Explicitly enable or disable copying.
  8. Avoid slicing. Consider Clone instead of copying in base classes.
  9. Prefer the canonical form of assignment.
  10. Whenever it makes sense, provide a no-fail swap (and provide it correctly).

Namespaces and Modules

  1. Keep a type and its nonmember function interface in the same namespace.
  2. Keep types and functions in separate namespaces unless they’re specifically intended to work together.
  3. Don’t write namespace using in a header file or before an #include.
  4. Avoid allocating and deallocating memory in different modules.
  5. Don’t define entities with linkage in a header file.
  6. Don’t allow exceptions to propagate across module boundaries.
  7. Use sufficiently portable types in a module’s interface.

Templates and Genericity

  1. Blend static and dynamic polymorphism judiciously.
  2. Customize intentionally and explicitly.
  3. Don’t specialize function templates.
  4. Don’t write unintentionally nongeneric code.

Error Handling and Exceptions

  1. Assert liberally to document internal assumptions and invariants.
  2. Establish a rational error-handling policy, and follow it strictly.
  3. Distinguish between errors and non-errors.
  4. Design and write error-safe code.
  5. Prefer to use exceptions to report errors.
  6. Throw by value, catch by reference.
  7. Report, handle, and translate errors appropriately.
  8. Avoid exception specifications.

STL: Containers

  1. Use vector by default. Otherwise, choose an appropriate container.
  2. Use vector and string instead of arrays.
  3. Use vector (and string::c_str) to exchange data with non-C++ APIs.
  4. Store only values and smart pointers in containers.
  5. Prefer push_back to other ways of expanding a sequence.
  6. Prefer range operations to single-element operations.
  7. Use the accepted idioms to really shrink capacity and really erase elements.

STL: Algorithms

  1. Use a checked STL implementation.
  2. Prefer algorithm calls to handwritten loops.
  3. Use the right STL search algorithm.
  4. Use the right STL sort algorithm.
  5. Make predicates pure functions.
  6. Prefer function objects over functions as algorithm and comparer arguments.
  7. Write function objects correctly.

Type Safety

  1. Avoid type switching; prefer polymorphism.
  2. Rely on types, not on representations.
  3. Avoid using reinterpret_cast.
  4. Avoid using static_cast on pointers.
  5. Avoid casting away const.
  6. Don’t use C-style casts.
  7. Don’t memcpy or memcmp non-PODs.
  8. Don’t use unions to reinterpret representation.
  9. Don’t use varargs (ellipsis).
  10. Don’t use invalid objects. Don’t use unsafe functions.
  11. Don’t treat arrays polymorphically.

Best Practices for C++ Development in Critical Systems

This document outlines comprehensive best practices derived from the MISRA C++:2023 guidelines for using C++17 in safety-critical and security-critical systems. These guidelines are intended to enhance reliability, maintainability, and robustness in software design and implementation.


General Principles

1. Use a Predictable Subset of C++

  • Adopt MISRA C++ as a subset of the C++17 standard.
  • Avoid reliance on undefined, unspecified, implementation-defined, and conditionally supported behavior.
  • Use static analysis tools to detect violations and enforce compliance.

2. Emphasize Code Safety and Security

  • Avoid constructs that introduce safety risks, such as uninitialized variables, unchecked operations, or invalid memory access.
  • Adhere to the principle of least privilege in all code design decisions.

3. Ensure Consistency and Readability

  • Follow a consistent style guide for variable naming, indentation, and layout.
  • Modularize code into small, well-defined functions with single responsibilities.

Best Practices for Language Features

Avoid Undefined and Unspecified Behavior

Rule 0.0.1: Functions shall not contain unreachable statements.

  • Rationale: Unreachable code often indicates a logical error and makes maintenance harder.
  • Compliant Example:
    int calculate(int value) {
        if (value > 0) {
            return value * 2;
        }
        return 0;
    }
  • Non-compliant Example:
    int calculate(int value) {
        return 0;
        value++; // Unreachable code
    }

Avoid Excessive Use of Implementation-Defined Features

Guideline: Minimize dependency on compiler-specific behavior for better portability and maintainability.

  • Best Practice:
    • Document all assumptions about compiler behavior explicitly.
    • Use portable libraries and abstractions where possible.

Best Practices for Code Quality

1. Minimize Unused Declarations

Rule 0.2.1: Variables with limited visibility should be used at least once.

  • Rationale: Unused declarations add noise to the code and may lead to confusion or maintenance errors.
  • Example:
    namespace {
        int value = 42; // Non-compliant if unused
    }
    
    void process(int a) {
        [[maybe_unused]] bool flag = (a > 0); // Compliant with attribute
        assert(flag);
    }

2. Avoid Writing to Unused Objects

Rule 0.1.1: A value should not be unnecessarily written to a local object.

  • Rationale: Writing to variables without observing their values wastes resources and might indicate logic errors.
  • Example:
    int process() {
        int temp = 0; // Non-compliant if temp is not used
        return 0;
    }

Best Practices for Floating-Point Arithmetic

Ensure Numerical Stability

Directive 0.3.1: Use floating-point operations carefully to avoid precision loss or overflow.

  • Validate inputs and handle special cases such as NaN and infinity explicitly.
  • Example:
    float compute(float value) {
        if (std::isnan(value)) {
            throw std::invalid_argument("NaN encountered");
        }
        return value * 2.0f;
    }

Best Practices for Exception Handling

Use Exception Handling Judiciously

Rule 4.18: Exceptions should not introduce runtime instability or obscure program flow.

  • Compliant Example:
    try {
        performOperation();
    } catch (const SpecificError& e) {
        handleError(e);
    } catch (...) {
        logGeneralError();
    }
  • Non-compliant Example:
    try {
        performOperation();
    } catch (...) {
        // Catch-all with no logging or handling
    }

Best Practices for Type Safety

Use Explicit Type Conversions

Rule 6.7.4: Avoid implicit conversions that might lead to data loss or undefined behavior.

  • Compliant Example:
    float value = 42.0f;
    int result = static_cast<int>(value); // Explicit cast
  • Non-compliant Example:
    float value = 42.0f;
    int result = value; // Implicit cast

Best Practices for Resource Management

Use RAII for Resource Safety

  • Encapsulate resource management using constructors and destructors to ensure deterministic cleanup.
  • Example:
    class ResourceHandler {
    public:
        ResourceHandler() { acquireResource(); }
        ~ResourceHandler() { releaseResource(); }
    };

Best Practices for Multi-threading

Avoid Data Races

Rule 4.12.1: Synchronize shared resources to avoid data races.

  • Compliant Example:
    std::mutex mutex;
    void safeIncrement(int& counter) {
        std::lock_guard<std::mutex> lock(mutex);
        ++counter;
    }

Documentation Practices

Use Consistent Commenting Style

  • Provide meaningful comments for complex logic, exceptions, and assumptions.
  • Avoid redundant comments that merely restate the code.

References

  • MISRA C++: 2023 Guidelines.
  • ISO/IEC 14882:2017 (C++17 Standard).
  • Industry standards such as IEC 61508, ISO 26262.

Notes

This document is a living guideline, intended to evolve with project needs and updates to coding standards. All team members should follow these practices and contribute suggestions for improvement.

This document is comprehensive and includes key examples, rationales, and rules based on the MISRA guidelines. It can be further expanded or customized to align with project-specific coding conventions.