Home > Net >  'std::bad_weak_ptr' error while using shared_from_this
'std::bad_weak_ptr' error while using shared_from_this

Time:01-30

Note: Before posting the question, I have gone through the existing questions on std::bad_weak_error while using shared_from_this to pass the shared_ptr of the existing shared_ptr instance to another method. None of them are similar to this as:

  • There is already an existing shared_ptr instance of the class created before trying to call shared_from_this()
  • The class inherits from std::enable_shared_from_this<> publically.

Here is the sample code to reproduce the error:


#include <iostream>
#include <memory>

class ILogger {
public:
    virtual ~ILogger() {}

    virtual void Log() = 0;
};

class LogManager;

class Logger : public ILogger {
public:
    Logger(std::shared_ptr<LogManager> logManager)
        : m_logManager(logManager)
    {
    }

    void Log() override
    {
        std::cout << "Dump logs";
    }

private:
    std::shared_ptr<LogManager> m_logManager;
};

class ILogManager {
public:
    virtual ~ILogManager() {}

    virtual std::shared_ptr<ILogger> GetLogger() = 0;
};

class LogManager : public ILogManager, public std::enable_shared_from_this<LogManager> {
public:
    virtual std::shared_ptr<ILogger> GetLogger()
    {
        return std::static_pointer_cast<ILogger>(std::make_shared<Logger>(this->shared_from_this()));
    }
};

class LogManagerFactory {
public:
    static ILogManager* Create()
    {
        auto logManager = new LogManager();
        return logManager;
    }
};

int main()
{
    auto logManager = std::shared_ptr<ILogManager>(LogManagerFactory::Create());
    auto logger = logManager->GetLogger();
}

The error:

Program returned: 139
terminate called after throwing an instance of 'std::bad_weak_ptr'
  what():  bad_weak_ptr

Link to code: https://godbolt.org/z/GTcafM449

CodePudding user response:

In order for the enable_shared_from_this subobject to be initialized, the shared_ptr constructor needs to know that the class in question inherits from enable_shared_from_this. Look at the expression that creates the shared pointer:

std::shared_ptr<ILogManager>(LogManagerFactory::Create());

The only class involved in this expression does not inherit from std::enable_shared_from_this<>. You are creating a shared pointer to ILogManager from a pointer to ILogManager. This is what the compiler generates code for – creating a shared_ptr for a class that does not inherit from enable_shared_from_this. The constructor is not aware that you are expecting it to initialize an enable_shared_from_this subobject.

The code works if you change the return type of LogManagerFactory::Create() from ILogManager* to LogManager*. You can keep the std::shared_ptr<ILogManager> part as long as the construction parameter brings LogManager into the picture.

Note: To be safer, LogManagerFactory::Create() should return either a unique_ptr or a shared_ptr instead of a raw pointer to clearly communicate that the caller gains ownership.

CodePudding user response:

When you run:

std::shared_ptr<ILogManager>(LogManagerFactory::Create())

shared_ptr only knows the pointer is an ILogManager which doesn't have enable_shared_from_this so doesn't set the weak pointer.

The solution (and safer code in general) is to create the shared_ptr in Create at which point the constructor can see the true type and enable_shared_from_this will therefore work:

    static std::shared_ptr<ILogManager> Create()
    {
        auto logManager = new LogManager();
        return std::shared_ptr<ILogManager>(logManager);
    }

Or more simply:

    static std::shared_ptr<ILogManager> Create()
    {
        return std::make_shared<LogManager>();
    }
  • Related