C++ Core Guidelines: Rules for Allocating and Deallocating

Contents[Show]

The guidelines has six rules for explicit memory allocation and deallocation. Six! Maybe you are surprised because there is a simple rule in modern C++: don't use new and delete. Obviously, the story is not so simple.

 

German Monopoly board in the middle of a game

Here are the six rules.

I will not write about the last two rules. First, the rule R.14 is not baked enough and second, the rule R.15 is quite special. If you want to learn more about overloading new and delete, you should read my posts to memory allocation and deallocation.

Before I dive into the rules, let me give you a little background which is necessary for understanding the rules. Creating an object in C++ with new consists of two steps.

  1. Allocate the memory for the object
  2. Constructs the object into the allocated memory

operator new or operator new [] makes the first step; the constructor the second step.

The same strategy applies to the destruction but the other way around. First, the destructor is called (if any) and then the memory is deallocated with operator delete or operator delete []. This two-step creation and destruction is the reason for the four rules. So, let's start.

R.10: Avoid malloc() and free()

What is the difference between new and malloc, or delete and free? The C-functions malloc and free do only half of the job. malloc allocates the memory and free only deallocates the memory. Either does malloc invoke the constructor nor does free invoke the destructor.

This means, if you use an object which was just created via malloc, you will get undefined behaviour.

// mallocVersusNew.cpp

#include <iostream>
#include <string>

struct Record{
  Record(std::string na = "Record"): name(na){}                 // (4)
  std::string name;
};

int main(){
    
    std::cout << std::endl;
    
    Record* p1 = static_cast<Record*>(malloc(sizeof(Record)));  // (1)
    std::cout << p1->name << std::endl;                         // (3)

    auto p2 = new Record;                                       // (2)
    std::cout << p2->name << std::endl;                           
    
    std::cout << std::endl;
   
}

 

I only allocate in (1) memory for my Record object. The result is that the output p1->name in (3) is undefined behaviour. In contrast, the call (2) invokes the constructor in line (4). Undefined behaviour just means that you can not make any assumption about the output of the program.

Depending on the used platform and the used GCC, the result of the program is entirely different.

  • GCC 4.8.5 produces a core dump on my local PC

mallocVersusNewGcc

  • GCC 4.9 (on cppreference.com) produces no output

mallocVersusNewOnline49

  • GCC 7.1 (cppreference.com) produces the expected output

mallocVersusNewOnline71

R.11: Avoid calling new and delete explicitly

You should keep this rule in mind. The emphasis in this rule lies on the word explicitly because using smart pointers or containers of the Standard Template Library give you object which use implicitly new and delete.

R.12: Immediately give the result of an explicit resource allocation to a manager object

This is the key ideas of a smart pointer such as std::unique_ptr<int> upInt(new int()) and will not hold in the counterexample from the guidelines. If the allocation of buffer fails the file handle will be lost.

void f(const std::string& name)
{
    FILE* f = fopen(name, "r");            // open the file
    std::vector<char> buf(1024);
    fclose(f);                             // close the file
}

 

R.13: Perform at most one explicit resource allocation in a single expression statement

This rule is a little bit tricky.

void func(std::shared_ptr<Widget> sp1, std::shared_ptr<Widget> sp2){
 ...
}

func(std::shared_ptr<Widget>(new Widget(1)), std::shared_ptr<Widget>(new Widget(2)));

 

This function call is not exception-safe and may, therefore, result in a memory leak. Why? The reason is that four operations must be performed to initialise the shared pointers.

  1. Allocate memory for Widget(1)
  2. Construct Widget(1)
  3. Allocate memory for Widget(2)
  4. Construct Widget(2)

The compiler is free to first allocate the memory for Widget(1) and Widget(2) and then construct both.

  1. Allocate memory for Widget(1)
  2. Allocate memory for Widget(2)
  3. Construct Widget(1)
  4. Construct Widget(2)

If one of the constructors throws an exception, the memory of the other object will not be automatically freed and we will get a memory leak.

It's quite easy to overcome this issue by using the factory function std::make_shared for creating an std::shared_ptr.  

func(std::make_shared<Widget>(1), std::make_shared<Widget>(2));

 

std::make_shared guarantees that the function will have no effect if an exception is thrown. The pendant function std::make_unique for creating an std::unique_ptr guarantees the same. 

What's next?

The next rules to resource management will follow the Rule R.11: avoid calling new and delete explicitly; therefore, the next post will be about the smart pointers std::unique_ptr, std::shared_ptr, and std::weak_ptr.

 

Thanks a lot to my Patreon Supporters: Eric Pederson, Paul Baxter, and Franco Amato.

 

Get your e-book at leanpub:

The C++ Standard Library

 

Concurrency With Modern C++

 

Get Both as one Bundle

cover   ConcurrencyCoverFrame   bundle
With C++11, C++14, and C++17 we got a lot of new C++ libraries. In addition, the existing ones are greatly improved. The key idea of my book is to give you the necessary information to the current C++ libraries in about 200 pages.  

C++11 is the first C++ standard that deals with concurrency. The story goes on with C++17 and will continue with C++20.

I'll give you a detailed insight in the current and the upcoming concurrency in C++. This insight includes the theory and a lot of practice with more the 100 source files.

 

Get my books "The C++ Standard Library" (including C++17) and "Concurrency with Modern C++" in a bundle.

In sum, you get more than 550 pages full of modern C++ and more than 100 source files presenting concurrency in practice.

 

Add comment


My Newest E-Books

Latest comments

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 1027

All 776948

Currently are 166 guests and no members online