What is a leak?

Each process is allowed to use limited resources: memory, handles and other objects - it's obvious. If it takes more and more memory, more and more handles, at some moment available resources has run out. The program will not run normally and exit.

Despite managed world (for example, .Net or Java), where the runtime takes over the managing resources (sounds great, but it doesn't mean that leaks can't be produced at all; they are still possible), C++ code must free memory and other resources once it doesn't need them any more.

If you noticed I didn't say "it must free them explicitly". It's great that very often C++ can help to free resources automatically due to the language features. We will learn how.

These are leaks, and "may be" leaks.

				for (;;) new int; // a naive leak, but leak
				// a leak as well;
				// don't ask me why people use C functions in C++ code:
				// they do
				char* dup = strdup("duplicate me");
				int* p = new int[100];
				delete p; // a leak? who knows
				// less naive leak
				class CBase { };
				class CDerived : CBase { int _data; }
				...
				CBase* base = new CDerived();
				delete base;

How not to make a leak? A naive view on the problem.

So, what to do not to make leaks? One can say: just free memory and that's all. Stop, stop, stop. Better to say "don't forget to free memory", right?

In every day job it's really very easy to forget to call delete, or free, especially if the code is going to be large. At one moment you just forget that this particular pointer must be freed.

So the question is how to write the code by such way where you have a very small chance to create a leak.

Fortunately, C++ has an idiom that is named "Resource acquisition is initialization", a very first concept that C++ developer should understand and use everywhere.

The bad news is that this is not all about leaks avoiding.

Leaks are fixed. What do you need any more?

				for (;;) { int* p = new int; delete p; }
				char* dup = strdup("duplicate me");
				free(dup);
				int* p = new int[100];
				delete[] p;
				class CBase {
				public:
					virtual ~CBase();
				};
				class CDerived : CBase {
					int _data;
				public:
					virtual ~CDerived();
				}
				...
				CBase* base = new CDerived();
				delete base;

RAII To The Rescue.

When an object goes out of scope, its destructor is called. The idea is to allocate a resource (or acquire it) in a constructor and free it in a destructor. You don't need to free resources each time in the code, you need to free resource in the single place: in the destructor.

In other words: constructor acquires, destructor releases.

This concept is called RAII - Resource acquisition is initialization.

Such classes are often called "smart pointers" despite the resource can be not only a memory block.

Do I need to write a "smart pointer" class myself?

Fortunately, in modern C++ you most probably never write such classes, but it's a good to know how they are implemented.

Wait, wait, you may ask but how I will use RAII in my code if I will not write such classes?! The answer is simple: C++ comes with ready-to-use classes. We will see which ones a bit later.

Moreover, talking about memory management you don't need RAII at all. Surprise! TODO:(add link to vector etc.).


Implementation of smart pointer.

Let's create a class that wraps a pointer to a memory block allocated from a heap. In constructor it will take ownership on a pointer, in destructor - frees the memory.

The usage is the following:

AutoFreeMemory<int> auto_free_mem(new int);			

How the implmentation might look like? Well, at the first glance it's quite simple:

template <class T>
class AutoFreeMemory
{
public:
	explicit AutoFreeMemory(T* p) : _p(p) {}
	~AutoFreeMemory() { delete _p; }

private:
	T* _p;
};			

What about copying objects of type AutoFreeMemory? Either it should be prohibited, or each instance that refers to the same pointer must share the same "state" - a piece of data that stores number of such instances, so that when the count becomes zero, then memory is freed.

The prohibition makes AutoFreeMemory light-weight class.

The idea of storing count of owners of a pointer is not so light-weght (locked operations are heavy). Here how we could implement shared data:

template <class T>
class SharedPtrData
{
	friend class SharedPtr<T>;

public:
	static SharedPtrData* Create(T* p)
	{
		return new SharedPtrData(p);
	}

	void AddRef() { InterlockedIncrement(&_refCount); }

	void Release()
	{
		if (0 == InterlockedDecrement(&_refCount))
			delete this;
	}

private:
	explicit SharedPtrData(T* p) : _p(p), _refCount(1) {}
	~SharedPtrData() { delete _p; }

private:
	T* _p;
	volatile ULONGLONG _refCount;
};			

All instances of the smart pointer class refer to the same SharedPtrData:

template <class T>
class SharedPtr
{
public:
	explicit SharedPtr() : _sharedData(nullptr) {}
	explicit SharedPtr(T* p) : _sharedData(SharedPtrData<T>::Create(p)) {}
	~SharedPtr() { if (_sharedData) _sharedData->Release(); }

	SharedPtr(const SharedPtr& other)
	{
		if (other._sharedData)
		{
			_sharedData = SharedPtrData<T>::Create(other._sharedData->_p);
			_sharedData->AddRef();
		}
		else
		{
			_sharedData = nullptr;
		}
	}

	SharedPtr& operator= (const SharedPtr& other)
	{
		if (_sharedData)
			_sharedData->Release();

		_sharedData = SharedPtrData::Create(_sharedData->_p);
		_sharedData->AddRef();

		return *this;
	}

private:
	SharedPtrData<T>* _sharedData;
};			

The good news is that C++ already has smart pointer classes: std::shared_ptr, std::weak_ptr.


Standard smart pointer classes

Fortunately, usually you need not to create a smart pointers as C++ already have such ones. Here the small review of them.

std::shared_ptr

std::shared_ptr is a smart pointer class those instances share ownership of the same pointer.
When last std::shared_ptr object that refers to a particular pointer is destroyed, memory is freed.

std::unique_ptr

std::unique_ptr is a smart pointer class that owns a pointer.
When std::unique_ptr object is copied to another one, the ownership is transferred.

std::weak_ptr

std::weak_ptr is stores a reference to an object, but std::weak_ptr object doesn't owner the object.

std::weak_ptr is initialized by std::unique_ptr. std::weak_ptr can tell whether shared pointer is freed or not (std::weak_ptr::expired()).

std::auto_ptr

std::auto_ptr is a smart pointer class that allocates an object and takes ownership on constructor.
std::auto_ptr deletes the object on destructor.

std::auto_ptr is deprecated in C++11, and removed in C++17. Use std::unique_ptr instead.

Using smart pointer to auto free resources.

Smart pointers are not limited to destroying objects. There are another resources: handles, file views, GDI and USER32 objects. Any resource can be freed automatically by smart pointers.

The key of the avoiding leaks is "developer doesn't need to free resource manually".

The key is a custom deleter that can be specified. Below see a set of samples: smart pointer way (a good way), and a way of manual freeing (bad way).

Auto closing file (FILE*).

Consider closing a file:

typedef std::unique_ptr<std::FILE, decltype(&std::fclose)> FilePtr;

FilePtr OpenLogFile(const std::string& path)
{
    return FilePtr(
        std::fopen(path.c_str(), "a"),
        &std::fclose);
}

int main(int argc, _TCHAR* argv[])
{
    FilePtr logFile = OpenLogFile("log.txt");

    if (logFile)
    {
        const char text[] = "some text";
        fwrite(text, sizeof(text), 1, logFile.get());
    }

	return 0;
}			

Auto closing handle (CloseHandle()).

Consider auto closing a handle:

typedef std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&::CloseHandle)> HandlePtr;

HandlePtr OpenLogFile(const std::wstring& path)
{
    return HandlePtr(CreateFileW(
        path.c_str(),
        GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        CREATE_ALWAYS,
        0,
        NULL), &::CloseHandle);
}

int main(int argc, _TCHAR* argv[])
{
    HandlePtr logFile = OpenLogFile(L"log.txt");

    if (INVALID_HANDLE_VALUE != logFile.get())
    {
        const char text[] = "some text";
        DWORD writtenBytes;
        WriteFile(logFile.get(), text, sizeof(text), &writtenBytes, NULL);
    }

	return 0;
}			

Auto freeing GDI resources.

Consider auto freeing HBITMAP, HDC, auto calling EndPaint():

LRESULT CALLBACK WndProc(
    HWND hWnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam)
{
    switch (message)
    {
        case WM_PAINT:
        {
            typedef std::unique_ptr<std::remove_pointer<HGDIOBJ>::type, decltype(&::DeleteObject)> GdiObjectPtr;
            typedef std::unique_ptr<std::remove_pointer<LPPAINTSTRUCT>::type, std::function<void(LPPAINTSTRUCT)> > AutoEndPaint;
            typedef std::unique_ptr<std::remove_pointer<HDC>::type, decltype(&::DeleteDC)> HDCPtr;

            PAINTSTRUCT ps;
            HDC wndDc = BeginPaint(hWnd, &ps);

            AutoEndPaint autoEndPaint(&ps,
                [=](LPPAINTSTRUCT ps)
                {
                    EndPaint(hWnd, ps);
                });

            RECT clientRect;
            GetClientRect(hWnd, &clientRect);

            HDCPtr memDC(CreateCompatibleDC(wndDc), &::DeleteDC);
            GdiObjectPtr memBmp(CreateCompatibleBitmap(memDC.get(), clientRect.right, clientRect.bottom), &::DeleteObject);
        
            SelectObject(memDC.get(), memBmp.get());

            const WCHAR text[] = L"SOME TEXT";
            TextOutW(memDC.get(), 0, 0, text, lstrlenW(text));

            BitBlt(wndDc, 0, 0, clientRect.right, clientRect.bottom, memDC.get(), 0, 0, SRCCOPY);

            break;
        }
    }
}			

Common mistakes

There are common mistakes that may cause leaks developers can make. Here we don't speak about good coding style.

Forgotten Virtual Destructors

A quick sample:

class Base
{
public:
    Base() { }
    ~Base() { }
};

class Derived : public Base
{
public:
    Derived() : _logFile(fopen("log.txt", "w"), [](FILE* f){ return fclose(f); })
    {
        const char* text = "hi!";
        fwrite(text, strlen(text), 1, _logFile.get());
    }
    
    ~Derived() { }

private:
    std::unique_ptr<std::remove_pointer<FILE>::type, decltype(&::fclose)> _logFile;
};

int main(int argc, _TCHAR* argv[])
{
    Base* obj = new Derived();
    delete obj;
}
			

Looks well? At the first glance, the code is great: std::unique_ptr is used to close file.

Sounds suddenly, but the problem is that std::unique_ptr::~std::unique_ptr() will not be called.

As destructor Base::~Base() is not virtual, delete obj calls destructor Base::~Base(), so Derived::~Derived() is not called, and destructor of the member _logFile is not called as well.

It's easy to understand by looking at the code generated by compiler:

mov ecx,dword ptr [ebp-0ECh]  
call Base::`scalar deleting destructor'

The fix is simple:

class Base
{
public:
    Base() { }
    virtual ~Base() { }
};
			

What does this change mean? Now the compiler know that the destructor must be called by pointer because it's virtual, and Derived::~Derived() is called now.

Well, what's the code is generated this time, as you see the destructor's address is taken from virtual table:

mov ecx,dword ptr [ebp-0ECh]  
mov edx,dword ptr [eax]  
call edx

Wrong Functions To Free Resources