Skip to content

Latest commit

 

History

History
133 lines (95 loc) · 4.23 KB

04-Considering_Safety.md

File metadata and controls

133 lines (95 loc) · 4.23 KB

Considering Safety

Const as Much as Possible

const tells the compiler that a variable or method is immutable. This helps the compiler optimize the code and helps the developer know if a function has a side effect. Also, using const & prevents the compiler from copying data unnecessarily. Here are some comments on const from John Carmack.

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

Consider Return By Value for Mutable Data, const & for Immutable

You don't want to have to pay a cost for copying the data when you don't need to, but you also don't want to have to safely return data in a threading scenario.

See also this discussion for more information: cpp-best-practices#21

Do not pass and return simple types by const ref

// 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 plan not to change passed value, declare them as const, but not const refs:

// 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? Because passing and returning by reference leads to pointer operations instead by much more faster passing values in processor registers.

Avoid Raw Memory Access

Raw memory access, allocation and deallocation, are difficult to get correct in C++ without risking memory errors and leaks. C++11 provides tools to avoid these problems.

// 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 guarantee contiguous memory layout of objects and can (and should) completely replace your usage of C-style arrays for many of the reasons listed for not using bare pointers.

Also, avoid using std::shared_ptr to hold an array.

Use Exceptions

Exceptions cannot be ignored. Return values, such as using boost::optional, can be ignored and if not checked can cause crashes or memory errors. An exception, on the other hand, can be caught and handled. Potentially all the way up the highest level of the application with a log and automatic restart of the application.

Stroustrup, the original designer of C++, makes this point much better than I ever could.

Use C++-style cast instead of C-style cast

Use the C++-style cast (static_cast<>, dynamic_cast<> ...) instead of the C-style cast. The C++-style cast allows more compiler checks and is considerable safer.

// Bad Idea
double x = getX();
int i = (int) x;

// Not a Bad Idea
int i = static_cast<int>(x);

Additionally the C++ cast style is more visible and has the possibility to search for.

But consider refactoring of program logic (for example, additional checking on overflow and underflow) if you need to cast double to int. Measure three times and cut 0.9999999999981 times.

Additional Resources

How to Prevent The Next Heartbleed by David Wheeler is a good analysis of the current state of code safety and how to ensure safe code.