Developing C++/CLI handle classes

Table of contents

Introduction

C++/CLI, one of the languages of the .NET Framework, is rarely used to develop large, stand-alone projects. Its main purpose is to create some assemblies for .NET interaction with the native (unmanaged) code. Accordingly, classes called handle ones, managed classes that have a pointer to the native class as a member, are widely used. Typically, such a handle class owns the corresponding native object, that is, it must delete it at the appropriate time. It is quite natural to make such a class disposable, that is, implement the System::IDisposable interface. The implementation of this interface in .NET must follow a special pattern called Basic Dispose [Cwalina]. A remarkable feature of C++/CLI is that the compiler takes on almost all the routine work of implementing this pattern, whereas in C# almost everything must be done manually.

1. Basic Dispose pattern in C++/CLI

There are two main ways to implement this pattern.

1.1. Defining a destructor and finalizer

In this case, the destructor and finalizer should be defined in the managed class, the compiler will do the rest.

public ref class X
{
    ~X() {/* ... */} // destructor
    !X() {/* ... */} // finalizer
    // ...
};

The compiler does the following:

  1. For the X class, it implements the System::IDisposable interface.
  2. In X::Dispose() provides a call to the destructor, a call to the destructor of the base class (if it exists) and a call to GC::SupressFinalize().
  3. Overrides System::Object::Finalize() where it provides a call to the finalizer and finalizers of base classes (if any).

Inheritance from System::IDisposable can be specified explicitly, but you cannot define X::Dispose() yourself.

1.2. Using stack semantics

The Basic Dispose pattern is also implemented by the compiler if the class contains a member of disposable type and it is declared using stack semantics. This means that the declaration uses the name of the type without a caret (‘^‘), and the initialization occurs in the initialization list of the constructor, and not with gcnew. Stack semantics is described in [Hogenson].

Let’s give an example:

public ref class R : System::IDisposable
{
public:
    R(/* parameters */); // constructor
    // ...
};
public ref class X
{
    R m_R; // not a R^ m_R

public:
    X(/* parameters */) // constructor
        : m_R(/* arguments */) // not a m_R = gcnew R(/* arguments */)
    {/* ... */}
    // ...
};

The compiler in this case does the following:

  1. For the X class, it implements the System::IDisposable interface.
  2. In X::Dispose() provides a call to R::Dispose() for m_R.

Finalization is determined by the appropriate R class functionality. As in the previous case, the inheritance from System::IDisposable can be specified explicitly, and you cannot define your own X::Dispose(). Of course, the class can contain other members declared using the stack semantics, and they are also provided with a call to Dispose().

2. Managed templates

And finally, another great feature of C++/CLI makes it as easy as possible to create handle classes. We are talking about managed templates. These are not generics, but real templates, as in classic C++, i.e. templates of managed classes. Instantiating such templates leads to the creation of managed classes that can be used as base classes or class members within an assembly. Managed templates are described in [Hogenson].

2.1. Smart pointers

Managed templates allow you to create classes like smart pointers that hold a pointer to the native object as a member and ensure its deletion in the destructor and finalizer. Such smart pointers can be used as base classes or members (of course, using the stack semantics) when developing handle classes that are automatically disposable.

Let’s give an example of such templates. The first is the base template, the second is intended for use as a base class and the third as a class member. These templates have a template parameter (native), designed to delete the native object. The default deleter deletes the object with the delete operator.

// native template, default deleter, T is a native class
template <typename T>
struct DefDeleter
{
    void operator()(T* p) const { delete p; }
};

// managed templates,
// smart pointers to a native object

// base template, T is a native class, D is a deleter
template <typename T, typename D>
public ref class ImplPtrBase : System::IDisposable
{
    T* m_Ptr;

    void Delete()
    {
        if (m_Ptr != nullptr)
        {
            D del;
            del(m_Ptr);
            m_Ptr = nullptr;
        }
    }

    ~ImplPtrBase() { Delete(); }
    !ImplPtrBase() { Delete(); }

protected:
    ImplPtrBase(T* p) : m_Ptr(p) {}

    T* Ptr() { return m_Ptr; }
};

// template to be used as a base class
template <typename T, typename D = DefDeleter<T> >
public ref class ImplPtr : ImplPtrBase<T, D>
{
protected:
    ImplPtr(T* p) : ImplPtrBase(p) {}

public:
    property bool IsValid
    {
        bool get() { return (ImplPtrBase::Ptr() != nullptr); }
    }
};

// template to be used as a class member
template <typename T, typename D = DefDeleter<T> >
public ref class ImplPtrM sealed : ImplPtrBase<T, D>
{
public:
    ImplPtrM(T* p) : ImplPtrBase(p) {}
    operator bool() { return ( ImplPtrBase::Ptr() != nullptr); }
    T* operator->() { return ImplPtrBase::Ptr(); }
    T* Get() { return ImplPtrBase::Ptr(); }
};

2.2. Usage example

class N // native class
{
public:
    N();
    ~N();
    void DoSomething();
    // ...
};

using NPtr = ImplPtr<N>; // base class

public ref class U : NPtr // managed handle class
{
public:
    U() : NPtr(new N()) {}
    void DoSomething() { if (IsValid) Ptr()->DoSomething(); }
    // ...
};

public ref class V // managed handle class, second version
{
    ImplPtrM<N> m_NPtr; // stack semantics

public:
    V() : m_NPtr(new N()) {}
    void DoSomething() { if (m_NPtr) m_NPtr->DoSomething(); }
    // ...
};

In these examples, the U and V classes are disposable without any additional effort; their Dispose() applies the delete operator to a pointer to N. The second version, using ImplPtrM<>, allows you to manage several native classes in the same handle class.

2.3. More complex options for finalization

Finalization is a rather problematic aspect of the work of .NET. In normal application scenarios finalizers should not be called; resource release occurs in Dispose(). But in emergency scenarios this can happen, and finalizers should work correctly.

2.3.1. Blocking finalizers

If the native class is in a DLL that is loaded and unloaded dynamically – using LoadLibrary()/FreeLibrary(), there may be a situation when, after unloading the DLL, there are objects that have references to instances of the native class. In this case, after some time the garbage collector will try to finalize them, and since the DLL is unloaded, the program is likely to crash. (A symptom is a crash a few seconds after the application closes visually.) Therefore, after unloading the DLL, the finalizers should be blocked. This can be achieved by a small modification of the basic template ImplPtrBase<>.

public ref class DllFlag
{
protected:
    static bool s_Loaded = false;

public:
    static void SetLoaded(bool loaded) { s_Loaded = loaded; }
};

template <typename T, typename D>
public ref class ImplPtrBase : DllFlag, System::IDisposable
{
    // ...
    !ImplPtrBase() { if (s_Loaded) Delete(); }
    // ...
};

After loading a DLL, call DllFlag::SetLoaded(true), and before unloading DllFlag::SetLoaded(false).

2.3.2. Using SafeHandle

The SafeHandle class implements rather complex and highly reliable finalization algorithm, see [Richter]. The ImplPtrBase<> template can be redesigned so that it uses SafeHandle. The rest of the templates do not need any changes.

using SH = System::Runtime::InteropServices::SafeHandle;
using PtrType = System::IntPtr;

template <typename T, typename D>
public ref class ImplPtrBase : SH
{
protected:
    ImplPtrBase(T* p) : SH(PtrType::Zero, true)
    {
        handle = PtrType(p);
    }

    T* Ptr() { return static_cast<T*>(handle.ToPointer()); }

    bool ReleaseHandle() override
    {
        if (!IsInvalid)
        {
            D del;
            del(Ptr());
            handle = PtrType::Zero;
        }
        return true;
    }

public:
    property bool IsInvalid
    {
        bool get() override
        {
            return (handle == PtrType::Zero);
        }
    }
};

Bibliography


Cwalina, Crzysztof and Abrams, Brad. Framework Design Guidelines, Second Edition. Addison-Wesley, 2008.


Hogenson, Gordon. С++/CLI: the Visual C++ Languge for .NET. APress, 2006.


Richter, Jeffrey. CLR via C#, 4 edition. Microsoft Press, 2012.

Copy semantics and resource management in C++

Table of contents

Introduction

Resource management is something that a C++ programmer must do all the time. Resources can include memory blocks, OS kernel objects, multi-threaded locks, network connections, database connections and just any object, created in dynamic memory. Access to a resource is carried out through a handle, a type of a handle is usually a pointer or one of its aliases (HANDLE, etc.), sometimes integer (UNIX file handles). After using a resource, it is necessary to release it, otherwise, sooner or later, an application that does not release resources (and possibly other applications) will face a shortage of resources. This problem is very acute; it can be said that one of the key features of the .NET, Java and several other platforms is a unified resource management system based on garbage collection.

Object-oriented features of C++ naturally lead to the following solution: the resource managing class contains a resource handle as a member, initializes the handle when the resource is acquired, and releases it in the destructor. However, the thing isn’t that simple as it seems to be. The main problem is coping semantics. If the class, that manages a resource, uses the copy constructor generated by the compiler by default, then, after copying the object, we get two copies of the handle of the same resource. If one object releases the resource, the second will possibly try to use or to release the released resource, which in any case is incorrect and can lead to so-called undefined behavior, for example, an application crash.

Fortunately, in C++, we can fully control the copying process by custom defining of a copy constructor and a copy assignment operator, which allows to solve the above problem, and usually not in one way. The implementation of copying should be closely linked to the resource release mechanism, and generally will be called copy-ownership policy. The well-known “Rule of The Big Three” states that if a programmer has defined at least one of the three operations — a copy constructor, a copy assignment operator, or a destructor — he must define all three operations. Copy-ownership policies specify the way to do it. There are four basic copy-ownership policies.

1. Basic copy-ownership policies

Before a resource is acquired or after it has been released, the handle must take on a special value indicating that it is not associated with the resource. It is usually zero, sometimes -1, casted to the type of handle. In any case, such a handle will be called null handle. The class that manages the resource must recognize the null handle and not try to use or release the resource in this case.

1.1. No copying policy

This policy is the simplest. In this case, it is simply forbidden to copy and assign class instances. The destructor releases the acquired resource. In C++, it is not difficult to prohibit copying; the class must declare, but not define, a private copy constructor and a private copy assignment operator.

class X
{
private:
    X(const X&);
    X& operator=(const X&);
// ...
};

Attempts to copy and assign are stopped by the compiler and the linker.

The C++11 standard offers a special syntax for this case:

class X
{
public:
    X(const X&) = delete;
    X& operator=(const X&) = delete;
// ...
};

This syntax is more demonstrative and gives more understandable compiler messages when attempting to copy and assign.

In the previous version of the standard library (C++98), the no copying policy used I/O stream classes (std::fstream, etc.), in Windows many classes from MFC (CFile, CMutex, CEvent, etc.). In the C++11 standard library, some classes for multi-threaded synchronization use this policy.

1.2. Exclusive ownership policy

In this case, during the implementing of copying and assignment, the resource handle moves from the source object to the target object, that means it remains in a single copy. After copying or assignment, the source object has a null handle and cannot use the resource. The destructor releases the acquired resource. In C++11, this is done in the following way: regular copying and copy assignment are prohibited in the manner described above, and move semantics are implemented, that is, the move constructor and the move assignment operator are defined. (More about move semantics on.)

class X
{
public:
    X(const X&) = delete;
    X& operator=(const X&) = delete;
    X(X&& src) noexcept;
    X& operator=(X&& src) noexcept;
// ...
};

Thus, the exclusive ownership policy can be considered an extension of the no copying policy.

In the C++11 standard library, this policy uses a smart pointer std::unique_ptr<> and some other classes, for example: std::thread, std::unique_lock<>, as well as classes that previously used the no copying policy (std::fstream, etc.). In Windows, MFC classes that previously used the no copying policy also has started to use the exclusive ownership policy. (CFile, CMutex, CEvent, etc.).

1.3. Deep copying policy

In this case, a programmer can copy and assign class instances. A copy constructor and a copy assignment operator must be defined, so that the target object copies the resource to itself from the source object. After that, each object owns its own copy of the resource, and can independently use, modify, and release the resource. The destructor releases the acquired resource. Sometimes for objects using the deep copying policy, the term “value objects” is used.

This policy can’t be applied to all resources. It can be used for resources associated with a memory buffer, such as strings, but it is not very clear how to apply it to OS kernel objects such as files, mutexes, etc.

The deep copy policy is used in all types of object strings, std::vector<>, and other containers in the standard library.

1.4. Shared ownership policy

In this case, one can copy and assign class instances. It is necessary to define the copy constructor and the copy assignment operator, in which a resource handle (as well as other data) is copied, but not the resource itself. After that each object has its own copy of the handle, can use, modify, but not release the resource, while there is at least one more object that has a copy of the handle. The resource is released after the last object that has a copy of the handle goes out of scope. The way it can be implemented is described below.

Smart pointers often use this policy; it is also natural to use it for immutable resources. In the C++11 standard library this policy is implemented by the smart pointer std::shared_ptr<>.

2. Deep copying policy – problems and solutions

Let’s consider a function template for exchange of states of objects of type T in the C++98 standard library.

template<typename T>
void swap(T& a, T& b)
{
    T tmp(a);
    a = b;
    b = tmp;
}

If the T type owns a resource and uses the deep copying policy, we have three operations of allocating a new resource, three operations of copying and three operations of releasing resources. Whereas in most cases this operation can be carried out without allocating new resources and copying, it is enough for objects to exchange internal data, including the resource handle. There are a lot of similar examples when it’s necessary to create temporary copies of a resource and release them right there. Such an inefficient implementation of daily operations has stimulated the search for solutions for optimization. Consider the main variants.

2.1. Copy-on-write

Copy-on-write (COW) can be taken as an attempt to combine a deep copy policy and a shared ownership policy. Initially, when the object is copied, the resource handle is copied without the resource itself, and for the owners the resource becomes shared and available in read-only mode. But as soon as some owner needs to modify the shared resource, the resource is copied and the owner works with its own copy. The implementation of the COW solves the problem of exchanging states: there is no additional resource allocation and copying. Using COW is quite popular when implementing strings; for example, CString (MFC, ATL). A discussion about possible ways to implement COW and problems that may arise can be found in [Meyers1], [Sutter]. [Guntheroth] proposes an implementation of COW using std::shared_ptr<>. There are problems when implementing COW in a multi-threaded environment, which is why in the C++11 standard library it is forbidden to use COW for strings, see [Josuttis], [Guntheroth].

The evolution of the COW idea leads to the following resource management scheme: the resource is immutable and is controlled by objects using the shared ownership policy. If necessary, a new, appropriately modified, resource is created and a new owner object is returned. This scheme is used for strings and other immutable objects on .NET and Java platforms. In functional programming it is used for more complex data structures.

2.2. Definition of the state exchange function for a class

Above it was shown how inefficiently the state exchange function, implemented straightforward, through copying and assignment, can work. But the function is widely-spread, for example, it is used by many algorithms of the standard library. In order the algorithms use not std::swap(), but another function, specifically defined for a class, one must perform two steps:

  1. To define in the class the member function Swap() (the name is not essential) that implements the exchange of states.
class X
{
public:
    void Swap(X& other) noexcept;
// ...
};

It’s necessary to make sure, that this function does not throw exceptions; in C++11 such functions must be declared as noexcept.

  1. In the same namespace as the X class (usually in the same header file) to define the free (non-member) function swap() as follows (name and signature fundamental):
inline void swap(X& a, X& b) noexcept { a.Swap(b); }

After that standard library algorithms will use it, not std::swap(). This provides a mechanism called argument dependent lookup (ADL). For more on ADL, see [Dewhurst1].

In the C++ standard library all containers, smart pointers, and other classes implement the state exchange function in the manner described above.

The Swap() member function is usually easy to define: it is necessary to apply a state exchange operation to the bases and members if they support it, and std::swap() otherwise.

The above description is somewhat simplified; more detailed information can be found in [Meyers2]. Some discussion of issues related to the state exchange function can also be found in [Sutter/Alexandrescu].

The state exchange function can be attributed to one of the basic operations of the class. Using it a programmer can gracefully define other operations. For example, a copy assignment operator is defined by copying and Swap() as follows:

X& X::operator=(const X& src)
{
    X tmp(src);
    Swap(tmp);
    return *this;
}

This pattern is called the “copy-and-swap” idiom, see [Sutter], [Sutter/Alexandrescu], [Meyers2] for more details. Its modification can be applied to the implementation of move semantics, see sections 2.4, 2.6.1.

2.3. Removing of intermediate copies by the compiler

Consider the class

class X
{
public:
    X(/* parameters */);
// ...
};

and the function

X Foo()
{
// ...
    return X(/* arguments */);
}

For a straight-line approach, returning from Foo() is accomplished by copying an instance of X. But some compilers can remove a copy operation from code; an object is created directly at the call point. This is called the return value optimization (RVO). RVO has been used by compiler developers for a long time and it is currently fixed in the C++ 11 standard. Although it’s a compiler which decides on RVO, a programmer can write code for its use. For this, it is desirable that the function has one return point and the type of the returned expression matches the type of the return value of the function. In some cases, it is advisable to define a special private constructor, called a “computational constructor”, for more details, see [Dewhurst2]. RVO is also discussed in [Meyers3] and [Guntheroth].

Compilers can remove intermediate copies in other situations as well.

2.4. Implementing move semantics

The implementation of move semantics consists of defining the move constructor that has rvalue-reference parameter to the source and the move assignment operator with the same parameter.

In the C++11 standard library the state exchange function template is defined as follows:

template<typename T>
void swap(T& a, T& b)
{
    T tmp(std::move(a));
    a = std::move(b);
    b = std::move(tmp);
}

In accordance with the overload resolution rules for functions that have parameters of rvalue-reference type (see Appendix A), in the case the T type has a move constructor and a move assignment operator, exactly they will be used, and temporary copies will not be created. Otherwise,the copy constructor and the copy assignment operator will be used.

The use of move semantics let a programmer to avoid the creation of temporary copies in a much wider context than the state exchange function described above. Move semantics is applied to any rvalue value, that is a temporary, unnamed value, as well as to a return value of the function if it is created locally (including the lvalue), and no RVO has been applied. In all these cases, it is guaranteed that the source object cannot be used in any way after performing the move. Move semantics also is used with a lvalue value to which the std::move() conversion is applied. But in this case, the programmer himself is responsible for how the source objects will be used after the move (for example std::swap()).

The C++11 standard library has been reworked to reflect move semantics. The move constructor and the move assignment operator, as well as other member functions, with rvalue-reference parameters, have been added to many classes. For example, std::vector<T> has an overloaded version of void push_back(T&& src). All-in-all it allows to avoid creating temporary copies in many cases.

Implementing move semantics does not override the definition of the state exchange function for the class. Specially defined state exchange function can be more efficient than the standard std::swap(). Moreover, the move constructor and the move assignment operator are very easily defined using the member function of the state exchange as follows (a variation of the “copy-and-swap” idiom):

class X
{
public:
    X() noexcept {/* null handle initialization */}
    void Swap(X& other) noexcept {/* state exchange */}

    X(X&& src) noexcept : X()
    {
        Swap(src);
    }

    X& operator=(X&& src) noexcept
    {
        X tmp(std::move(src)); // moving
        Swap(tmp);
        return *this;
    }
// ...
};

For the move constructor and the move assignment it is highly desirable to make sure that they do not throw exceptions, and, accordingly, they are declared as noexcept. This allows a user to optimize some operations of the containers of the standard library without violating the strong exception guarantee (for more details, see [Meyers3] and [Guntheroth] ). The proposed pattern gives such a guarantee, provided that the default constructor and the state exchange member function do not throw exceptions.

The C++11 standard considers that a compiler automatically generates a move constructor and a move assignment operator; to do this, they must be declared using the "= default" construct.

class X
{
public:
    X(X&&) = default;
    X& operator=(X&&) = default;
// ...
};

The operations are implemented by sequentially applying the move operation to bases and class members if they support moving, and copy operation otherwise. Obviously, this variant is far from always acceptable. Raw handles do not support moving, but they usually cannot be copied. If certain conditions are met, the compiler may independently generate such a move constructor and a move assignment operator, but it is better not to use this opportunity, these conditions are rather confusing and can be easily changed when the class is refined. See [Meyers3] for details.

In general, implementing and using move semantics is not so elementary. The compiler can apply copying where the programmer expects movement. Let us cite a few rules that allow to eliminate or at least reduce the probability of such a situation.

  1. To prohibit copying if possible.
  2. To declare a move constructor and a move assignment operator as noexcept.
  3. To implement move semantics for base classes and members.
  4. To apply the std::move() conversion to the function parameters of rvalue-reference type.

Rule 2 was discussed above. Rule 4 is related to the fact that named rvalue-references are lvalues (see also Appendix A). This can be illustrated by defining a move constructor.

class B
{
// ...
    B(B&& src) noexcept;
};

class D : public B
{
// ...
    D(D&& src) noexcept;
};

D::D(D&& src) noexcept
    : B(std::move(src)) // moving
{/* ... */}

Another example of this rule is given above, when defining a move assignment operator. Implementing move semantics is also discussed in section 6.2.1.

2.5. Emplacement vs insertion

The idea of emplacement is similar to the idea of underlying RVO (see section 2.3), but it is applied not to the return value of the function, but to the input parameters. In the traditional insertion of an object into a container, an object is first created (often temporary), then copied or moved to the storage location, after which the temporary object is deleted. When emplacing, the object is created immediately in the storage location, only the arguments of the constructor are passed. Containers of the C++11 standard library have the emplace(), emplace_front(), emplace_back() member functions that work this way. Naturally, they are template member functions with a variable number of template parameters — variadic templates, since the number and the type of constructor parameters are not known in advance. In addition to this other advanced C++11 features are used – perfect forwarding and universal references.

Emplacement has the following advantages:

  1. For objects, that do not support moving, the copy operation is excluded.
  2. For objects, that support moving, placement is almost always more efficient.

Here is an example where the same problem is solved in different ways.

std::vector<std::string> vs;
vs.push_back(std::string(3, 'X')); // insertion
vs.emplace_back(3, '7');           // emplacement

In the case of insertion, a temporary std::string is created, then it is moved to the storage location and after that the temporary object is deleted. When emplacing, the object is created immediately in the storage location. Emplacement looks more succinct and it is likely to be more efficient. Scott Meyers discusses emplacement, perfect forwarding and universal references in details in [Meyers3].

2.6. Summary

One of the main problems of the classes, that implement a deep copying policy, is the creation of temporary copies of a resource. None of the described methods completely solves this problem and does not completely replace any other method. In any case, a programmer must recognize such situations and write the correct code, taking into account the problem described and the possibilities of the language. The simplest example is the passing parameters to the function: it is necessary to pass by reference, and not by value. This error is not recognized by the compiler, but either unnecessary copying occurs or a program does not work as intended. Another example is related to using a move: a programmer must strictly observe the conditions under which the compiler chooses a move, otherwise copying will be used “silently”.

The described problems allow us to make the following recommendation: it is necessary to avoid the deep copying policies whenever possible; the real need for deep copying occurs very rarely, this is confirmed by the programming experience on the .NET and Java platforms. As an alternative, an implementation of deep copying using a special function can be suggested, the traditional name for such functions is Clone() or Duplicate().

If, nevertheless, implementing the class that manages a resource, it is decided to use the deep copying policy, the following steps can be recommended in addition to implementing copying semantics:

  1. To define the state exchange function.
  2. To define a move constructor and a move assignment operator.
  3. To define necessary member functions and free functions with parameters of rvalue-references type.

On the .NET and Java platforms, the basic copy-ownership policy is the shared ownership policy, but if necessary, one can implement the deep copy policy, for example, in .NET it is necessary to implement the IClonable interface. As noted above, the need for this occurs quite rarely.

3. Possible variants for implementing shared ownership policy

It is quite easy to implement the shared ownership policy for a resource, that has an internal reference counter. In this case, when copying an object – a resource owner – the reference counter is incremented, and in the destructor it is decremented. When its value reaches zero, the resource releases itself. The internal reference counter uses basic Windows OS resources: OS kernel objects, managed through HANDLE, and COM objects. For kernel objects, the reference counter is incremented by the DuplicateHandle() function, and decremented by the CloseHandle() function. For COM objects, the member functions IUnknown::AddRef() and IUnknown::Release() are used. The smart pointer CComPtr<> from ATL library controls COM objects that way. For UNIX file handles, opened using C standard library functions, the reference counter is incremented by the _dup() function, decremented using the file close function.

In the standard C++ library the smart pointer std::shared_ptr<> also uses a reference count. But the object, controlled by this smart pointer, may not have an internal reference counter, so a special hidden object is created. It is called a control block and it manages the reference counter. It is some additional overhead. The smart pointer std::shared_ptr<> is described in detail in [Josuttis], [Meyers3].

The use of the reference counter has a congenital defect: if resource owner objects have reciprocal references to each other, their reference counters will never be zero (the problem of circular references). In some cases, resources cannot have reciprocal references (for example, OS kernel objects) and therefore this problem is not relevant, but in other cases, the programmer himself must monitor such situations and take the necessary efforts. When using std::shared_ptr<>, it is suggested to use the auxiliary smart pointer std::weak_ptr<> for this purpose. See more in [Josuttis], [Meyers3].

Andrei Alexandrescu considers the implementing a joint ownership policy using a doubly linked list of owner objects [Alexandrescu]. Herbert Schildt describes (and lists the complete code) implementation, based on the combination of a doubly linked list and the [Schildt] reference counter. Implementations, based on a doubly linked list, also cannot release resources that have circular references.

Description of more complicated schemes for removing unused objects (garbage collectors) can be found in [Alger].

The implementation of the shared ownership policy should also include the possibility of multi-threaded access to the owner objects. This topic is discussed in [Josuttis] and [Alexandrescu].

The shared ownership policy is the main copy-ownership policy on the .NET and Java platforms. The component of the executing environment, which deals with the removal of unused objects, is called garbage collector, runs periodically and uses complex algorithms for analyzing an object graph.

4. Exclusive ownership policy and move semantics

Safe implementation of the exclusive ownership policy became possible only after C++ began to support the rvalue-references and move semantics. In the C++98 standard library there was a smart pointer std::auto_ptr<> that implemented the exclusive ownership policy. However, it had limited use, in particular, it could not be stored in containers. The fact is that it could move the pointer from the object which still needed this pointer (simply speaking, to steal). In C++11, rules for using rvalue references ensure that data can only be moved from a temporary unnamed object, otherwise occurs a compilation error. In the C++11 standard library std::auto_ptr<> is deprecated and it is recommended to use std::unique_ptr<> instead. This smart pointer implements the exclusive ownership policy based on move semantics, it is described in [Josuttis], [Meyers3].

Some other classes also support the exclusive ownership policy: I/O stream classes (std::fstream, etc.), some classes for working with threads (std::thread, std::unique_lock<>, etc.). In MFC, the classes that previously used the no copying policy (CFile, CEvent, CMutex, etc.) has started to apply the exclusive ownership policy.

5. No copying policy – quick start

At first glance the no copying policy severely limits the programmer, but it turns out that many objects do not need to be copied. Therefore, when designing the class that manages a resource, as an initial solution, we can recommend to choose the no copying policy. If copying is required, the compiler will immediately detect it; after that a programmer can analyze what copying is needed for (and whether it’s required at all) and make the necessary improvements. In some cases, for example, when passing through a call stack, the reference can be applied. If it is necessary to store objects in the containers of the standard library, a programmer can use smart pointers to objects, created in dynamic memory. In general, the use of dynamic memory and smart pointers is a universal variant that can help in other cases. A more complicated way is the implementation of move semantics. Details are discussed in section 6.

Negligent software design, which does not implement any copy-ownership policy, often does not lead to runtime errors, because the objects that own the resource are not actually copied. In this case, the no copying or another copy-ownership policy doesn’t change anything. But, nevertheless, it still has to be done, one must always write the proper code, even if in some context the wrong code does not show its defects. The wrong code will “shoot” sooner or later.

6. The life cycle of the resource and the object-owner of the resource

In many cases it is important to understand how the life cycle of a resource and its object-owner correlate. Naturally, this is closely related to the copy-ownership policy. Let’s have a look at several variants.

6.1. Resource acquisition is initialization

In the simplest case the life cycle of a resource and the object-owner are the same, which means, for the class that manages the resource, the following conditions are met:

  1. Resource acquisition occurs only in a class constructor. If the acquisition fails, an exception is thrown, and the object is not created.
  2. Resource release occurs only in the destructor.
  3. Copying and moving are prohibited.

Constructors of such classes usually have the parameters, necessary to acquire the resource, and, accordingly, there is no default constructor. In the standard C++11 library some classes are implemented to support multi-threaded synchronization.

This resource management scheme is one of the variants of the “Resource acquisition is initialization” (RAII) idiom. RAII is widely observed in many books and on the Internet (and is often interpreted slightly differently or simply not quite clearly), see, for example, [Dewhurst1]. The above variant can be called “strict” RAII. In such a class it is natural to make the resource handle a constant member, and, accordingly, it’s possible to use the term immutable RAII.

6.2. Extended variants for managing the life cycle of a resource

A class, implemented in accordance with the RAII idiom, is ideal for creating simple, short-lived objects that have a block lifetime. However, if an object must be either a member of another class, or an element of an array or some container, the absence of a default constructor, as well as copy-move semantics, can cause many problems for a programmer. In addition, sometimes a resource is acquired in several steps, and their number may not be known in advance, which makes it extremely difficult to implement a resource acquisition in the constructor. Consider possible solutions to this problem.

6.2.1. Extended life cycle of a resource

Let’s agree, that the class, managing a resource, supports the extended life cycle of the resource if the following conditions are met for it:

  1. There is a default constructor which doesn’t acquire the resource.
  2. There is a mechanism for resource acquisition after creating an object.
  3. There is a resource release mechanism before the object is destroyed.
  4. The destructor releases the acquired resource.

In the standard C++11 library the extended life cycle of a resource is supported by strings, containers, smart pointers, and also some other classes. But keep in mind that the clear() member function, implemented in strings and containers, destroys all stored objects, but may not release the reserved memory. For the complete release of all resources it is necessary to take additional measures. For example, you can use shrink_to_fit(), or simply assign an object, created by the default constructor (see below).

The class, implemented in accordance with the RAII idiom, can be refined, using a standard pattern so that it will support the extended life cycle of the resource. To do this one must additionally define a default constructor, a move constructor, and a move assignment operator.

class X
{
public:
// RAII
    X(const X&) = delete;            // copy ban
    X& operator=(const X&) = delete; // assignment ban

    X(/* parameters */);             // acquires a resource
    ~X();                            // releases the resource
// add
    X() noexcept;                    // zeros the resource handle
    X(X&& src) noexcept              // move constractor
    X& operator=(X&& src) noexcept;  // move assignment operator
// ...
};

After this, the extended life cycle of the resource is implemented quite simply.

X x;                    // creating an "empty" object
x = X(/* arguments */); // resource acquisition
x = X(/* arguments */); // new resource acquisition, old releasing
x = X();                // resource releasing

This is exactly how the std::thread is implemented.

As it was shown in section 2.4, a standard way of defining a move constructor and a move assignment operator uses the member state exchange function. Besides, the member state exchange function makes it very plain to define individual member functions to acquire and release the resource. Here is the corresponding new variant:

class X
{
// RAII
// ...
public: // add an variant with a member state exchange function
    X() noexcept;
    X(X&& src) noexcept;
    X& operator=(X&& src) noexcept;
    void Swap(X& other) noexcept;  // exchanges states
    void Create(/* parameters */); // acquires a resource
    void Close() noexcept;         // releases the resource
// ...
};

X::X() noexcept {/* the resource handle zeroing */}

Definition of a move constructor and a move assignment operator:

X::X(X&& src) noexcept : X()
{
    Swap(src);
}

X& X::operator=(X&& src) noexcept
{
    X tmp(std::move(src)); // moving
    Swap(tmp);
    return *this;
}

Definition of the individual member functions to acquire and release the resource:

void X::Create(/* parameters */)
{
    X tmp(/* arguments */); // resource acquisition
    Swap(tmp);
}

void X::Close() noexcept
{
    X tmp;
    Swap(tmp);
}

It should be noted that in the pattern described the resource is always acquired in the constructor, and released in the destructor, the member state exchange function plays a purely technical role. This simplifies the coding and makes its more reliable for the acquisition and release of a resource, since the compiler takes over part of the implementation logic, especially in the destructor.

In the examples above the definitions of the copy assignment operator and the resource acquisition member function used the copy-and-swap idiom, according to which a new resource is acquired first, then the old one is released. This scheme provides so-called strong exception guarantee: if an exception occurs during the acquisition of a resource, the object won’t change its state (transactional semantics). In certain situations another scheme may be more preferable: the old resource is released first, then a new one is acquired. This variant provides a weaker exception guarantee, called the basic one: if an exception occurs during the acquisition of a resource, the object will not necessarily remain in the same state, but the new state will be correct. In addition, when defining a copy assignment operator using this scheme, a self-assignment check is desirable. Details of exception safety guarantees are discussed in [Sutter], [Sutter/Alexandrescu], [Meyers2].

So, the transition from RAII to the extended life cycle of the resource is very similar to the transition from the no copying policy to the exclusive ownership policy.

6.2.2. Single resource acquisition

This variant can be considered as intermediate between RAII and the extended life cycle of the resource. We will say that the class, that manages the resource, uses a single resource acquisition if the following conditions are met for it:

  1. There is a default constructor which doesn’t acquire the resource.
  2. There is a mechanism for resource acquisition after creating an object.
  3. Repeated resource acquisition is prohibited. If such an attempt occurs, an exception is thrown.
  4. Resource release occurs only in the destructor.
  5. Copying is prohibited.

This is “almost” RAII, the only difference is the possibility for a formal separation of an object creating operation and resource acquisition. Such a class may have a move constructor, but not a move assignment operator, otherwise the condition of clause 3 will be violated. This simplifies the objects storage in standard containers. Despite some unfinished business, this variant is quite practical.

6.2.3. Increase indirectness

Another approach to extending the life cycle of a resource is to increase the level of indirectness. In this case, the RAII object itself is considered as a resource, and the pointer to it will be a resource handle. Resource acquisition is reduced to creating an object in dynamic memory, and, releasing it – to deleting the object. As a class that manages such a resource, one can use one of the standard library’s smart pointers. A copy-ownership policy is determined by a smart pointer. This method is much simpler than that described in section 6.2.1, the only drawback is the more intensive use of dynamic memory.

7. Summary

The class, that manages a resource, should not have a copy constructor, a copy assignment operator, and a destructor generated by the compiler. These member functions must be defined according to a copy-ownership policy.

There are 4 basic copy-ownership policies:

  1. No copying policy.
  2. Exclusive ownership policy.
  3. Deep copying policy.
  4. Shared ownership policy.

The state exchange function should be attributed to the basic operations of the class. It is used in the standard library algorithms, as well as for defining other member functions of the class: a copy assignment operator, a move constructor and a move assignment operator, member functions to acquire and release the resource.

The definition of a move constructor and a move assignment operator allows to optimize classes that use a deep copying policy. For the classes that use the no copying policy, this allows to expand the copy-ownership policy, implement a more flexible resource lifecycle management scheme and simplify the placement of objects in containers.

When designing the class that owns a resource, we can recommend the following sequence of actions. It is appropriate to begin with no copying policy. If compilation reveals the need for copying and cannot be avoided by simple means, it’s requied to create objects in dynamic memory and use smart pointers to control their lifetime (see section 6.2.3). If this variant does not suit, the move semantics have to be implemented (see section 6.2.1). One of main consumers of copying is the containers of the standard library; the implementation of move semantics removes almost all restrictions on their use.

As mentioned above, implementation of the deep copying policy is better to be avoided, the real need for it rarely arises. Implementation of the shared ownership policy is also a bad idea; instead, use the smart pointer std::shared_ptr<>.

Appendices

Appendix A. Rvalue-references

Rvalue-references are a type of regular C++ references, the difference is in the initialization rules and the overloaded resolution rules for functions, that have parameters of the rvalue-references type. The rvalue-references type for type T is denoted by T&&.

For examples the class is used:

class Int
{
    int m_Value;

public:
    Int(int val) : m_Value(val) {}
    int Get() const { return m_Value; }
    void Set(int val) { m_Value = val; }
};

Like regular references, rvalue-references must be initialized.

Int&& r0; // error C2530: 'r0' : references must be initialized

The first difference between rvalue-references and regular C++ references is that they cannot be initialized using lvalue. Example:

Int i(7);
Int&& r1 = i; // error C2440: 'initializing' : cannot convert from 'Int' to 'Int&&'

For correct initialization, you need to use rvalue:

Int&& r2 = Int(42); // OK
Int&& r3 = 5;       // OK

or lvalue must be explicitly casted to an rvalue-reference type:

Int&& r4 = static_cast<Int&&>(i); // OK

Instead of the cast operator, the function std::move() is usually used (more precisely, a function template), which does the same (the header file <utility>).

Rvalue references can be initialized with a rvalue of a built-in type, which is forbidden for regular references.

int&& r5 = 2 * 2; // OK
int& r6 = 2 * 2;  // error

After initialization, the rvalue-references can be used as regular references.

Int&& r = 7;
std::cout << r.Get() << '\n'; // Output: 7
r.Set(19);
std::cout << r.Get() << '\n'; // Output: 19

Rvalue-references are implicitly casted to regular references.

Int&& r = 5;
Int& x = r;           // OK
const Int& cx = r;    // OK

Rvalue-references are rarely used as independent variables, they are usually applied as function parameters. In accordance with the initialization rules, if a function has parameters of rvalue-references type, it can be called only for rvalue arguments.

void Foo(Int&&);

Int i(7);
Foo(i);            // error, lvalue argument
Foo(std::move(i)); // OK
Foo(Int(4));       // OK
Foo(5);            // OK

If there are several overloaded functions, overload resolution rules claim that for the rvalue arguments the version with a parameter of type rvalue-reference takes benefits over a version with a parameter of type regular reference or regular reference to a constant, although the latter may be a valid variant. And this rule is the second key feature of rvalue-reference.

The function with a parameter, passed by value, and an overloaded version, that has a parameter of type rvalue-reference, will be ambiguous for the rvalue arguments.

For example, consider overloaded functions

void Foo(Int&&);
void Foo(const Int&);

and several variants to call them

Int i(7);
Foo(i);            // Foo(const Int&)
Foo(std::move(i)); // Foo(Int&&)
Foo(Int(6));       // Foo(Int&&)
Foo(9);            // Foo(Int&&)

Attention is drawn to one important point: a named rvalue-reference itself is an lvalue.

Int&& r = 7;
Foo(r);            // Foo(const Int&)
Foo(std::move(r)); // Foo(Int&&)

This should be taken into account when defining functions, having the parameters of type rvalue-reference. Such parameters are lvalue and may require the use of std::move(). See an example of a move constructor and a move assignment operator in section 2.4.

Another C++11 innovation, related to rvalue-references, is the reference qualifiers for non-static member functions. They allow to overload by type (lvalue/rvalue) of the hidden parameter this.

class X
{
public:
    X();
    void DoIt() &;  // this points to lvalue
    void DoIt() &&; // this points to rvalue
// ...
}; 

X x;
x.DoIt();   // DoIt() &
X().DoIt(); // DoIt() &&

Appendix B. Move semantics

For classes, owning a resource like a memory buffer and useing the deep copy policy (std::string, std::vector<>, etc.), the problem of preventing the creation of temporary copies of the resource is essential . One of the most effective ways to solve this problem is to implement move semantics. For this, the move constructor, that has a parameter of type rvalue-reference to the source, and the move assignment operator with the same parameter are defined. When they are implemented, the data, including the resource handle, is copied from the source object to the target object, and the resource handle of the source object is reset; the resource is not copied. In accordance with the above overload resolution rules, in the case when the class has a copy constructor and a move constructor, the move constructor will be used for initialization with rvalue, and the copy constructor for initialization with lvalue. If the class has only a move constructor, the object can be initialized only with rvalue. The assignment operators works the same. Move semantics is also used when returning a value, created locally, from a function (including an lvalue), unless RVO has been applied.

Bibliography

[Alexandrescu]
Alexandrescu, Andrei. Modern C++ Design. Addison-Wesley, 2001.

[Alger]
Alger, Jeff. C++ for Real Programmers. Academic Press, 1998.

[Dewhurst1]
Dewhurst, Stephen C. C++ Common Knowledge. Addison-Wesley, 2005.

[Dewhurst2]
Dewhurst, Stephen C. C++ Gotchas: avoiding common problems in coding and design. Addison-Wesley, 2002.

[Guntheroth]
Guntheroth, Kurt. Optimized C++. O’Reilly, 2016.

[Josuttis]
Josuttis, Nicolai M. The C++ Standard Library – A Tutorial and Reference, 2nd Edition. Addison-Wesley, 2012.

[Meyers1]
Meyers, Scott. More effective C++: 35 New Ways to Improve Your Programs and Designs. Addison-Wesley, 1996.

[Meyers2]
Meyers, Scott. Effective C++: 55 Specific Ways to Improve Your Programs and Designs, 3rd Edition. Addison-Wesley, 2005.

[Meyers3]
Meyers, Scott. Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14. O’Reilly, 2015.

[Schildt]
Schildt, Herbert. The Art of C++. McGraw-Hill, 2004.

[Sutter]
Sutter, Herb. Exceptional C++. Addison-Wesley, 2005.

[Sutter/Alexandrescu]
Sutter, Herb and Alexandrescu, Andrei. C++ Coding Standards. Addison-Wesley, 2005.

About smart pointers in C++

About smart pointers in С++

An application can use different resources – memory, handles, network connections and other stuff that is better to free after usage. C++ is a flexible language that allows a C++ developer to do a lot but makes him care about many things, including freeing resources. This article is dedicated to smart pointers.

Well, what can you do with resources?

To allocate but not to free

You may just live resources alone: as soon as a process exits, OS frees resources itself. It might look incorrect, but this approach can be used for the resources that are involved all the lifetime of a process.

It quite often occurs to memory – static variables and singletons are common things. It’s such a good deal – any time a program can call a singleton and check it. A singleton is always here.

Freeing manually

Like in good old times – if one had opened a file it was necessary to close it. Objects’ lifetime can be long and non-trivial because of the multithreading and exceptions. It’s hard not to make a mistake.

Reference counting

The idea is to count references to an object and once the count sets to zero, you free the object. There is a standard std::shared_ptr class in C++, below we will examine it.

Any problems? Yes, some. If object A has a reference to object B and vice a versa (circular references), then the reference counts of both A and B never set to zero, and thus A and B never get free .

Despite the primitiveness and the non-universality, this approach is widely used in languages like Python and frameworks like .Net due to the garbage collection.

However, in C++ you can use standard class std::weak_ptr to avoid circular references problem. Let’s talk about std::weak_ptr a bit later.

Garbage collection

On the one hand, a program doesn’t free objects explicitly, that makes a code easier,  particulary, when one deals with tree structures, lists, and, of course, lock-free structures.

On the other hand, a developer can rely on the garbage collection only in case of the memory freeing: if, for example, a file is opened, it’s much better not to wait when the garbage collector decides to close the handle, but to close the handle explicitly.

С++: std::unique_ptr

In C++ it’s easy to create an object on the stack:

struct DebugObject {
    int m_i;

    DebugObject(int i) : m_i(i) {
        std::cout << "DebugObject(" << m_i << ") created\n";
    }

    virtual ~DebugObject() {
        std::cout << "DebugObject(" << m_i << ") deleted\n";
    }
};

void f() {
    DebugObject p(1);
}

Once f() is finished, p goes out of scope and it gets destroyed.

Unfortunately, there are some obstacles. An object be destroyed right before exiting from f(), this can’t be changed. If you want to return the objects from a function, or to save it, a copy of the object is created.

It’s ok for lightweight objects like a number pair but imagine an array with billion items – you can’t do it this way. A stack is limited, copying large objects again and again takes a lot of time.

Nevertheless, destruction of an object on a stack when it goes out of scope is really a powerful feature of C++. Even if an exception is thrown in f(), the object p gets destroyed anyway and its destructor is called. Imagine a wrapper class that is created on a stack, which stores an object pointer (allocated from a heap), and destroys the object when “wrapper” goes out of scope. This wrapper class is std::unique_ptr.

With the help of std::unique_ptr you can write:

void f() {
        // It's correct in с++11
        std::unique_ptr<DebugObject> p1(new DebugObject(1));
        // make_unique is available in с++14
        auto p2 = std::make_unique<DebugObject>(2);
}

In this code all objects are freed correctly, and it doesn’t matter what might happen during the execution.

Custom deleter

It would be strange if there were no way to specify a custom function to delete an object:

void customDelete(DebugObject *p) {
     std::cout << "CustomDelete()\n";
     delete p;
}

void f() {
    std::unique_ptr<DebugObject, void(*)(DebugObject*) > p(new DebugObject(3), customDelete);

    p.reset(nullptr);
    p.reset(new DebugObject(4));

    std::cout << "raw pointer size:" << sizeof (DebugObject*) << "\n";
    std::cout << "unique_ptr with deleter size:" << sizeof (p) << "\n";
}

It’s important to mention a reset method: it overwrites an old pointer with a new pointer. The deleter function is called for an old pointer if it’s not null.

The custom deleter function std::unique_ptr let free not only objects, but any kind of resources, e.g. handles.

An example of a closing handle:

struct FILEDeleter {
    void operator()(FILE *pFile) {
        if (pFile)
            fclose(pFile);
    }
};

using FILE_unique_ptr = std::unique_ptr<FILE, FILEDeleter>;

FILE_unique_ptr make_fopen(const char* fname, const char* mode) {
    FILE *fileHandle = fopen(fname, mode);
    // fileHandle will be nullptr if error
    return FILE_unique_ptr(fileHandle);
}

void f() {
    std::cout << "unique_ptr with stateless struct as deleter: " << sizeof (FILE_unique_ptr) << "\n";
}

Notice that FILEDeleter is stateless: FILE_unique_ptr stores just one pointer of a file.

Returning from a function

std::unique_ptr can be returned from a function. Due to move semantics std::unique_ptr transfers object owning, set pointer to null, and the object itself is not destroyed on exit from makeObj(). The object will be destroyed on exit from function f():

std::unique_ptr<DebugObject> makeObj(int n) {
    return std::make_unique<DebugObject>(n);
}

void f() {
    std::unique_ptr<DebugObject> p = std::move(makeObj(5));
    std::unique_ptr<DebugObject> p2 = makeObj(6);
    std::unique_ptr<DebugObject> p3(makeObj(7));

    auto p4 = std::move(p);
    // auto p5 = p; //compilation error!
}

Get raw pointer

void f() {
    auto p = std::make_unique<DebugObject>(8);

    std::cout << "DebugObject.m_i == " << p->m_i << "\n";
    DebugObject *inner_p = p.get();
}

It is simple. It is worth mentioning that p continues storing a pointer, and DebugObject will be deleted on exit. In case you don’t want to destroy an object, use the method release. As in case of usual pointers, it’s possible to access to fields and methods via operator ->.

void f() {
    DebugObject *raw_ptr = nullptr;
    {
        auto p = std::make_unique<DebugObject>(9);
        raw_ptr = p.release();
    }
    std::cout << "Object alive yet\n";
    delete raw_ptr;
}

Casting to bool

It’s quite handy that std::unique_ptr is similar to raw pointer – one can cast it to bool (if it’s nullptr, the result is false; otherwise it is true):

auto p = std::make_unique<DebugObject>(10);

if (p) {
    std::cout << "p isn't null\n";
}

Object owning

To make things simple, it’s a good idea to decide who owns an object, for example:

class Owner {
private:
    DebugObject *m_p;
    // DebugObject is an abstract class, size is unknown
public:

    Owner(DebugObject *p) : m_p(p) {
    };

    ~Owner() {
        delete m_p;
    }
};

Well, let’s rewrite the code using std::unique_ptr:

class Owner {
private:
    std::unique_ptr<DebugObject> m_p;
public:

    Owner(DebugObject *p) : m_p(p) {
    };
};

The code is much simplier now because std::unique_ptr is stored by value, not by a pointer, the destructor Owner::~Owner() calls the destructor of m_p.

A few words about object owning

In Rust there are so-called linear types: https://en.wikipedia.org/wiki/Substructural_type_system. Such types can be used only once. Speaking about object owning, if someone owns an object, it’s possible to transfer ownership to another owner only once .

Unfortunately, there is no such a feature in C++. One can make two unique pointers of the same object:

void f() {
    auto* p = new DebugObject(11);

    Owner o1(p);
    Owner o2(p);
    // bad way
}

In this case one object is destroyed twice which is wrong, of course. To avoid such thing it’s better to wrap a pointer by std::unique_ptr not at an arbitrary place, but at the start position instead.

class Owner2 {
private:
    std::unique_ptr<DebugObject> m_p;
public:

    Owner2(std::unique_ptr<DebugObject> &&p) : m_p(std::move(p)) {
        std::cout << "I'm owner of " << (m_p ? "object" : "nullptr") << "\n";
    };
};

void f() {
    auto p = std::make_unique<DebugObject>(12);

    Owner2 realOwner(std::move(p));
    Owner2 fakeOwner(std::move(p));
    // at least it doesn't crash
}

In С++11 rvalue references and move semantics has been introduced.

std::vector<int> a{1, 2, 3};
std::vector<int> b = a;
a.push_back(4);

assert((b == std::vector<int>{1, 2, 3}));
assert((a == std::vector<int>{1, 2, 3, 4}));

For example, standard std::vector stores pointers of the array beginning, end and the count.

The content of the vector a (i.e. the whole array of data inside it) will be copied, and b will contain another pointers. But if vector a is not planned to be used later, we can name it rvalue reference and, instead of making a deep copy, just copy the pointers. Actually, the poitners are not just copied, but replaced between a and b.

To convert an object to rvalue reference explicitly you can write:

std::vector<int> c{1, 2, 3};
std::vector<int> d = std::move(c);

// don't use objects after they are moved
c.push_back(4);

assert((d == std::vector<int>{1, 2, 3}));
assert((c == std::vector<int>{4}));

So, at the second line values of empty vector b and non empty vector a are exchanged. a has been rvalue and it has been changed by assignment.

What are we talking about? Alike, std::unique_ptr transfers owning being assigned, and we can’t make two similar std::unique_ptr-s from a single one: one of them will contain nullptr.

In the sample above fakeOwner will contain nullptr and realOwner will be the only owner.

Of course, it would be great if C++ prohibited the usage of p after the first std::move(p), but, unfortunately, C++ doesn’t support it (yet?).

Well, at least we can’t delete an object twice, and a developer suspects that something is going wrong when seeing at std::move(p).

A small excursus on Rust: the idea of owning an object helps to implement state machine – for example, it’s possible to have class “closed file” with the only method “open” and the class “opened file” with the methods for reading and closing. And the method “open” will take the ownership of closed file and return opened file, and vice a versa. This approach allows to use resources with more number of states than usual “resource is allocated, resource is freed”.

C++: std::shared_ptr

As mentioned above, std::shared_ptr is a wrapper of a pointer with the reference count.

void f() {
    std::shared_ptr<DebugObject> ptr1(new DebugObject(0));

    std::cout << ptr1.use_count() << "\n"; 
    // 1
    std::cout << ptr1.unique() << "\n"; 
    // true

    {
        auto ptr2 = ptr1;
        std::cout << ptr1.use_count() << "," << ptr2.use_count() << "\n";
        // 2, 2
        std::cout << ptr1.unique() << "," << ptr2.unique() << "\n";
        // false, false
    }

    std::cout << ptr1.use_count() << "\n";
    // 1
    std::cout << ptr1.unique() << "\n";
    // true
}

The difference between std::shared_ptr and std::unique_ptr is that std::shared_ptr is heavier: it stores also a pointer to an additional object, a control block, that contains the reference count (count of std::shared_ptr instances that points to the same object)

When the reference count become zero, the object gets destroyed. The reference count is changed in a thread-safe manner, that’s why working with std::shared_ptr is slower than with raw pointers and std::unique_ptr.

std::make_shared

std::shared_ptr<DebugObject> p1(new DebugObject(0));
auto p2 = std::make_shared<DebugObject>(1);

The second approach is more efficient. Why? In the first case memory is allocated twice (two pieces) – for DebugObject and for a control block.

std::make_shared immediately allocates single piece of memory that contains both an object and a control block. The disadvantage is that it’s possible to free this memory for both the object and the control block.

Wait, why it’s necessary to keep control block alive after destroying the object? The thing is that a control block also contains a count of weak references.

std::weak_ptr

void f() {
    std::weak_ptr<DebugObject> w;
    {
        std::shared_ptr<DebugObject> p(new DebugObject(2));
        w = p;
        std::cout << w.use_count() << "," << p.use_count() << "\n";
        // 1, 1
        std::cout << (w.expired() ? "true" : "false") << "\n";
        // false
    }
    std::cout << (w.expired() ? "true" : "false") << "\n";
    // true
    std::cout << w.use_count() << "\n";
    // 0
}

In the sample above the reference count has become zero and the object is destroyed. But the count of weak references is not 0 and the control block still exists (it will be deleted only when the count of weak reference becomes zero).

The object pointer is hidden inside std::weak_ptr and can’t be accessed from the outside. To get it one must call the method lock – it returns std::shared_ptr and the object can’t be destroyed suddenly (if it has been destroyed already, the std::shared_ptr with nullptr will be returned).

std::weak_ptr can be used to avoid circular references – for example, А contains std::shared_ptr of B, Bstd::weak_ptr of A.

Thread safety

  • std::unique_ptr is not thread safe. If one calls e.g. std::move in the two threads at the same time, one can get two pointers of the same object. However, std::unique_ptr is fast.
  • std::shared_ptr – a reference count in a control block is incremented and decremented thread-safely. But there is one thing: std::weak_ptr contains two pointers (of the object and of the control block), and reading it in one thread, while someone is writing it in another thread, is not a good idea. On the other hand, one can read std::shared_ptr from several threads at the same time. std::shared_ptr guarantees that, when all owners are destroyed, the object is destroyed as well.

For reading/writing std::shared_ptr from different threads use synchronization mechanism: mutexes and others. Also there are standard helpers like std::atomic_load, std::atomic_store and std::atomic_exchange.

These helpers use blockings, and std::atomic_exchange is atomic only from the points of view of others “atomic_*” functions. If one of them is used, it’s better to use them everywhere.

About possible leaks

In complex expressions the order of execution is not defined:

func(std::shared_ptr<Lhs>(new Lhs("foo")), std::shared_ptr<Rhs>(new Rhs("bar")));

It’s possible, that Lhs(“foo”) has just been created but shared_ptr(..) hasn’t been called yet, and creation of a new Rhs(“bar”) will throw an exception. So Lhs(“foo”) will not be deleted as a smart pointer hasn’t been created yet.

If using std::make_shared(“foo”) everything will be fine:

func(std::make_shared<Lhs>("foo"), std::make_shared<Rhs>("bar"));

Another variant is not to write a long expression but split it onto three parts – creation pointers of Lhs, Rhs and call of func(…).
In this case smart pointer of Lhs is created earlier than constructor of Rhs is called. The same for std::unique_ptr.

Read more about that here: https://stackoverflow.com/questions/20895648/difference-in-make-shared-and-normal-shared-ptr-in-c

Conslusion

The usage of std::unique_ptr simplifies a code and reduces the possibility of memory leaks. This kind of pointers is very lightweight – its size is equal to the raw pointer’s size (In case when the custom deleter is used – the size is equal to the size of two raw pointers). You can use it wherever the only owner of an object is supposed.

If the reference count is required, one can use std::shared_ptr along with std::weak_ptr to avoid circular references problems.

Deleaker 2018.27: new command line tool, bug fixes

The new Deleaker 2018.27 is available with new command line tool and several bug fixes.

The new command line tool makes Deleaker a part of a test suite.

Our customers used to ask if we were going to add support of running Deleaker from the command line? Such a feature is quite useful when it’s necessary to run Deleaker with an automated testing suite. It will require Deleaker to be launched from the command line.

That is why DeleakerConsole.exe, a command line tool that runs a process under Deleaker and produces leaks reports, has been created.

You can find the tool comprehensive documentation here.

Please write us if you find some features missed.

Case sensitivity.

Deleaker uses the Debug Help Library to retrieve names of source files. Sometimes these names are returned by the library in lower case.

Is it a problem? Sometimes.

For example, it causes a weird problem if you use SVN. It is a case sensitive tool: SVN can store two files with names that differ in case only.

Imagine, Deleaker shows source file name in incorrect case (the Debug Help Library has given this name!), then one opens the file from Deleaker in Visual Studio, edits it and finally saves. The case of the file name has changed. In fact the file has got a new name from SVN’s point of view.

Now Deleaker cares about the file name case and opens it using the correct name.

As usual updated version available on the download page.

Deleaker 2018.22: Resource Usage Graph

We’re excited to announce you the latest update of Deleaker that includes the resource usage graph.

Developers often ask if it’s possible to watch the memory usage of a process. Now with the help of the graph one can visually inspect the memory usage, GDI and USER32 resources, handles:

In Visual Studio the graph respects current color scheme:

You can easily switch between different resource types:

As usual updated version available on the download page.