C++ Best Practices for Safety

Click the blue text
C++ Best Practices for Safety
Follow us

Due to changes in the public account’s push rules, please click “Looking” and add “Star Mark” to get exciting technical shares at the first time

Source from the internet, please delete if infringed

C++ Best Practices:
1. Tools
2. Code Style
3. Safety (This Article)
4. Maintainability
5. Portability and Multithreading
6. Performance
7. Correctness and Scripts

Safety

Use const whenever possible

Use const to modify variables or methods to inform the compiler that these are immutable, helping the compiler optimize the code and assisting developers in understanding whether a function has side effects. Additionally, using const & can prevent the compiler from copying unnecessary data. John Carmack’s comments on const are worth reading.
// Bad Idea
class MyClass
{
public:
  void do_something(int i);
  void do_something(std::string str);
};

// Good Idea
class MyClass
{
public:
  void do_something(const int i);
  void do_something(const std::string &str);
};

Carefully consider return types

  • Getters (Member Variable Read API)
    • Normally, using & or const & to return values when reading member variables can significantly improve performance
    • Returning by value is more beneficial for thread safety; if the returned value is meant for copying, there will be no performance loss
    • If the API returns values using covariant types, it must return & or *
  • Temporary values and local values
    • Always return by value
Reference: https://github.com/lefticus/cppbestpractices/issues/21 https://twitter.com/lefticus/status/635943577328095232

Do not use const references to pass and return simple types

// Very Bad Idea
class MyClass
{
public:
  explicit MyClass(const int& t_int_value)
    : m_int_value(t_int_value)
  {
  }

  const int& get_int_value() const
  {
    return m_int_value;
  }

private:
  int m_int_value;
}
Instead, pass and return simple types by value. If you do not intend to change the passed value, declare them as const, but do not declare as const reference:
// 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;
}
Why do this? Because passing and returning by reference can lead to pointer operations, while passing by value is handled in processor registers, which is faster.

Avoid accessing raw memory

It is difficult to correctly handle access, allocation, and deallocation of raw memory in C++ without memory errors and leaks, and C++11 provides tools to avoid these issues.
// Bad Idea
MyClass *myobj = new MyClass;

// ...
delete myobj;

// 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>(); 

// ...
// myobj is automatically freed for you whenever it is no longer used.

Use std::array or std::vector instead of C-style arrays

Both of these methods guarantee a contiguous memory layout of objects and can (and should) completely replace C-style arrays, which is also one of the many reasons to avoid using raw pointers.
Additionally, avoid using std::shared_ptr to hold arrays.

Use exceptions

Return values (such as boost::optional) can be ignored, which may lead to crashes or memory errors if not checked, while exceptions cannot be ignored. On the other hand, exceptions can be caught and handled. Exceptions may rise to the highest level of the application to be caught, logged, and trigger the application to restart.
One of the designers of C++, Stroustrup, has talked about this topic: Why use exceptions?

Use C++ style type casting instead of C style type casting

Use C++ style casts (static_cast<>, dynamic_cast<>, etc.) instead of C-style casts. C++ style casts allow for more compiler checks and are quite safe.
// Bad Idea
double x = getX();
int i = (int) x;

// Not a Bad Idea
int i = static_cast<int>(x);
Additionally, C++ style casting is more explicit and search-friendly.
However, if you need to cast from double to int, consider refactoring the program logic (e.g., perform additional checks for overflow and underflow). Avoid situations where you measure three times and cut 0.9999999999981 times.

Do not define variadic functions

Variadic functions can accept a variable number of arguments, the most famous example being printf(). While such functions can be defined, they may pose security risks. The use of variadic functions is not type-safe, and incorrect input parameters may lead to undefined behavior that could result in security issues. If you are using a compiler that supports C++11, consider using variadic templates instead.
Reference: It is technically possible to make typesafe C-style variadic functions with some compilers

Additional Resources

David Wheeler’s How to Prevent The Next Heartbleed provides a great analysis of the current state of code safety and how to ensure code safety.
If you are over 18 years old and find learning 【C language】 too difficult? Want to try other programming languages? I recommend you learn Python, currently, a Python zero-based course worth 499 yuan is available for free, limited to 10 places!



▲ Scan the QR code - Get it for free
C++ Best Practices for Safety
Click to read the original text for more information

Leave a Comment