Dangerous std::shared_ptr

Dangerous std::shared_ptr

std::shared_ptr is very useful and flexible smart pointer provided by C++ standard library. But we should use it very carefully, because it is very easy to make a mistake in a real project. One of the possible mistakes happened with me recently. Just look at code example below. This code is truncated example of real code from a real project. And this example works as expected.

// Transport level class like pipes, sockets etc.
// Instance of this class is accessed from different places.
class Transport : public std::enable_shared_from_this<Transport>
{
public:
};
 
// Main class creates a new instance of transport.
class MainClass
{
public:
    MainClass()
    {
        m_transport = std::make_shared<Transport>();
    }
public:
    const std::shared_ptr<Transport>& GetTransport()
    {
        return m_transport;
    }
private:
    std::shared_ptr<Transport> m_transport;
};
 
// All other classes use transport from MainClass.
class OtherClass
{
public:
    OtherClass(MainClass& owner) : _transport(owner.GetTransport())
    {
    }
private:
    std::shared_ptr<Transport> _transport;
};
 
// usage example
int main()
{
    MainClass mainClass;
    OtherClass otherClass(mainClass);
}

Later I've refactored the code of MainClass as follows. I had reasons to do this.

class MainClass
{
public:
    MainClass()
    {
        m_transport = std::make_shared<Transport>();
    }
public:
    Transport* GetTransport()
    {
        return m_transport.get();
    }
private:
    std::shared_ptr<Transport> m_transport;
};

Project was compiled successfully without any warnings, but in runtime I've got an error. When we have a small code example then obviously that this code contains a mistake. When we have a big solution from many projects then it is very difficult to catch this mistake. When I've found, and fixed my error I started thinking. In this situation I have three different ways to avoid this type mistakes:

  • Be careful. This solution is not for me, because I know that it is much easier said than done.

Replace std::shared_ptr by another smart pointer implementation. This is acceptable for me if I cannot find another solution.

  • Generate compile time error for the situation above. In this case I can keep using std::shared_ptr and do not need to rewrite all places, where std::shared_ptr was used.

I examined std::shared_ptr implementation and I found that it uses different internal classes for different ways creation of std::shared_ptr. _Ref_count_obj template class is used for creation through std::make_shared and _Ref_count template class is used for creation from an existing pointer. Currently, it is very easy to create a macro, which can disable one of shared_ptr constructors for a specific class:

// Visual Studio 2012 and higher versions do not use tr1 namespace
#if _MSC_VER >= 1700
#define SHARED_PTR_DISABLE_CREATION_FROM_POINTER(type) namespace std\
{ template<> class _Ref_count<type> {};}
#else
#define SHARED_PTR_DISABLE_CREATION_FROM_POINTER(type) namespace std\
{ namespace tr1 { template<> class _Ref_count<type> {}; } }
#endif

This macro defines an empty template specialization of _Ref_count for a specified type. If the template specialization is used then compile time error will be generated. Currently to check that std::shared_ptr for class Transport is used properly I need only one additional line:

class Transport : public std::enable_shared_from_this<Transport>
{
public:
};
 
SHARED_PTR_DISABLE_CREATION_FROM_POINTER(Transport)

So, I think that it is not ideal solution and it will be better to replace std::shared_ptr by another smart pointer implementation. From opposite side many people know std::shared_ptr and with help of this trick we can avoid of mistakes. I check that my macro works properly with VS2010, VS2012, VS2013 and VS2015, but I can not guarantee that it will work for another versions of Visual Studio, because it is based on internal implementation of Microsoft.



What is the reason to use Transport* GetTransport() signature? Taking raw pointer from shared_ptr X and warping it into another shared_ptr Y create a new smart pointer without any link X <-> Y. C++ protects you from mixing raw pointers and smart pointers. The only way you could mix raw pointers with shared_ptr is initialize or reset shared_ptr with a new created (dettached) raw pointer.

Like
Reply

To view or add a comment, sign in

More articles by Viktar Marmysh

  • Chronicle of a Failure: How We Restored a MetaTrader 5 Database on a Live Server

    It all started as the most ordinary routine task. That day, Tom was handling a standard MetaTrader 5 server migration.

    2 Comments
  • C++ строки для максимальной производительности в торговле

    Strings Очевидно, что крайне сложно представить себе приложение, которое бы так или иначе не работала с текстом. Да…

  • Hand made MT4/5 Bridge: Quotes Filtration

    Many years ago I worked on filtering of incoming quotes. The chain of different transformations was long and took into…

  • Hand made MT4/5 Bridge: фильтрация котировок

    Много лет назад я работал над фильтрацией входящих котировок. Цепочка различных преобразований была длинной и учитывала…

  • Hand made MT4/5 Bridge: quotes aggregation

    Bridge: quotes aggregation While talking about quotes routing we dealt with different quotes transformations. In…

  • Hand made MT4/5 Bridge: symbols normalization

    Bridge: symbols normalization In case Bridge is connected only to one and the only one liquidity provider, the…

  • Hand made MT4/5 Bridge: part 2

    Bridge: quotes routing For successful processing of quotes within the system it is needed that modules, responsible for…

  • Hand made MT4/5 Bridge: part 1

    When talking about Forex, the phrase “over-the-counter Forex market” is used. As Forex market is over-the-counter, we…

    1 Comment
  • Hand made MT4/5 Bridge

    The construction and development of successful Forex Brokerage depends on many factors. One of them is the cost of…

  • Hand made MT4/5 Bridge: part 4

    При обсуждении маршрутизации котировок мы коснулись различных преобразований котировок. В действительности возможности…

Others also viewed

Explore content categories