C++ Best Practices

repository·master·Indexed 27 days ago

https://github.com/cpp-best-practices/cppbestpractices

A collaborative collection of C++ best practices designed to help developers write safer and more efficient code. The resource covers automated development frameworks, including recommendations for source control, build tools like CMake, package managers such as Conan and Vcpkg, and CI providers. It provides detailed guidance on compiler warning configurations for GCC, Clang, and MSVC, as well as the use of static analysis tools, sanitizers, fuzz testing, and code coverage analysis.

Tokens
8.8K
Snippets
19
Records
73
Agent score
93%

What's inside cppbestpractices

  1. Overview of Static Analysis Tools

    master

    Various tools are available for catching bugs and code smells in C++:

    • SonarLint / SonarQube / SonarCloud: SonarLint is an IDE plugin; SonarQube/SonarCloud run in build pipelines to gate PRs.
    • Clang-based tools: clang-tidy (linting), clang-check, and include-what-you-use (header management).
    • Coverity Scan: Integrates with CI (Travis CI, AppVeyor) for automated analysis.
    • PVS-Studio: Specialized bug detection for C/C++ and C#.
    • cppclean: Focuses on problems that slow down large codebases.
    • CppDepend: Visualizes code dependencies and manages design rules.
    • Clang's Static Analyzer: Can be used via scan-build from CMake or through CodeChecker.
    • OCLint: Open-source tool for improving code quality.
    • IKOS: NASA-developed analyzer based on Abstract Interpretation.
  2. Initialize member variables using member initializer lists

    master

    Always initialize member variables using the member initializer list in constructors. This is more efficient for non-POD types as it avoids calling the default constructor before assignment.

    For C++11 and later, you can also assign default values directly to members using {} (preferred) or =. Brace initialization {} is preferred because it prevents narrowing conversions at compile-time.

    class MyClass
    {
    public:
      MyClass(MyOtherClass t_myOtherClass)
        : m_myOtherClass(t_myOtherClass)
      {
      }
    
    private:
      MyOtherClass m_myOtherClass;
    };
    class MyClass
    {
    public:
      MyClass(int t_value)
        : m_value(t_value)
      {
      }
    
    private:
      int m_value;
    };
  3. Optimize template usage to avoid build bloat

    master

    Templates can significantly increase compiled code size and build times if not used carefully. Follow these guidelines:

    1. Avoid Unnecessary Template Instantiations: Instantiating many templates or templates with excessive code increases the workload on the compiler.
    2. Avoid Recursive Template Instantiations: Recursive templates place a heavy load on the compiler and make code harder to maintain. Use variadic expansions and folds (C++11 and later) as an alternative.
  4. Avoid Typeless Interfaces by using strong types

    master

    Avoid using generic types like std::string for parameters that have specific semantic meanings (e.g., file paths, patterns, or IDs). Using generic types can lead to accidental implicit conversions and logic errors. Instead, use specific types like std::filesystem::path or std::regex.

    For even higher correctness, consider using a typesafe library to prevent implicit conversions between different types that share the same underlying representation (like two different string-based IDs).

  5. Include local files using double quotes

    master

    Use double quotes "" for local files and angle brackets <> for system includes. Using <> for local files requires extra compiler directives and violates standard conventions.

    #include <string>    // System include
    #include "MyHeader.hpp" // Local file
    #include <string>
    #include "MyHeader.hpp"
  6. Pass and return simple types by value

    master

    Avoid passing and returning simple types (like int, double, etc.) by const &. Passing and returning by reference forces pointer operations, whereas passing by value allows the compiler to use much faster processor registers. If you want to ensure a passed value is not modified, declare it as const but pass it by value.

    // Good Idea
    class MyClass
    {
    public:
      explicit MyClass(const int t_int_value)
        : m_int_value(t_int_value)
      {
      }
    
      int get_int_value() const
      {
        return m_int_value;
      }
    
    private:
      int m_int_value;
    };
  7. Avoid raw memory access using Smart Pointers

    master

    Avoid manual memory management (new and delete) to prevent memory errors and leaks. Use C++11/C++14 smart pointers instead:

    • std::unique_ptr: For exclusive ownership.
    • std::shared_ptr: For reference-counted objects.
    • std::make_unique (C++14) and std::make_shared: Preferred for creating smart pointers safely.
    // Good Idea
    auto myobj = std::make_unique<MyClass>(constructor_param1, constructor_param2); // C++14
    auto myobj = std::unique_ptr<MyClass>(new MyClass(constructor_param1, constructor_param2)); // C++11
    auto mybuffer = std::make_unique<char[]>(length); // C++14
    auto mybuffer = std::unique_ptr<char[]>(new char[length]); // C++11
    
    // or for reference counted objects
    auto myobj = std::make_shared<MyClass>();