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>();
}