Matthew
Matthew

Reputation: 850

how to create unique_ptr from inherited parent class

Is there a way to create a unique_ptr from one of the inherited classes?

I need to be able to "register" MouseListeners with a manager but I cannot figure out how to create the unique_ptr of the inherited MouseListener.

The error is that it cannot find a conversion from Window * to MouseListener. I have tried a static_cast but that produces other errors. I have also tried passing in a raw pointer to the addMouseListener which did work but errored when you close the program as I think it did not create the appropriate memory which causes the delete to fail.

Also using std::move() transfers ownership which causes the listener to not fire the event.

// Window.h
class Window : public MouseManager, public MouseListener {
public:
    Window::Window(std::string title, int32_t width, int32_t height) {
        ...
        this->addMouseListener(std::make_unique<MouseListener>(this)); // ERROR
    }
};

// MouseManager.h
void MouseManager::addMouseListener(std::unique_ptr<MouseListener> listener) {
    m_listeners.emplace_back(listener);
}

// MouseListener.h

MouseListener() = default;

virtual ~MouseListener() = default;
MouseListener(const MouseListener& listener) = default;
MouseListener(MouseListener&& listener) noexcept ;
MouseListener& operator=(const MouseListener& listener) = delete;
MouseListener& operator=(MouseListener&& listener) = delete;

Error output

In file included from /Users/Programmer/CLionProjects/StormEngine/Engine/Window/Window.cpp:5:
In file included from /Users/Programmer/CLionProjects/StormEngine/Engine/Window/Window.h:8:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:3141:32: error: no matching constructor for initialization of 'MouseListener'
    return unique_ptr<_Tp>(new _Tp(_VSTD::forward<_Args>(__args)...));
                               ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/Programmer/CLionProjects/StormEngine/Engine/Window/Window.cpp:17:33: note: in instantiation of function template specialization 'std::__1::make_unique<MouseListener, Window *>' requested here
    this->addMouseListener(std::make_unique<MouseListener>(this));
                                ^
/Users/Programmer/CLionProjects/StormEngine/Engine/Window/../Events/Listeners/MouseListener.h:19:5: note: candidate constructor not viable: no known conversion from 'Window *' to 'const MouseListener' for 1st argument; dereference the argument with *
    MouseListener(const MouseListener& listener) = default;
    ^
/Users/Programmer/CLionProjects/StormEngine/Engine/Window/../Events/Listeners/MouseListener.h:20:5: note: candidate constructor not viable: no known conversion from 'Window *' to 'MouseListener' for 1st argument; dereference the argument with *
    MouseListener(MouseListener&& listener) noexcept ;
    ^
/Users/Programmer/CLionProjects/StormEngine/Engine/Window/../Events/Listeners/MouseListener.h:16:5: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
    MouseListener() = default;
    ^

Update:

de-referencing this adds a new problem where to add the variable listener you have to std::move() which causes the change of ownership which as stated above causes the events not to fire.

Upvotes: 0

Views: 1773

Answers (2)

eerorika
eerorika

Reputation: 238491

std::make_unique<MouseListener>(this)

This allocates a new MouseListener instance, with this as an argument to the constructor. As the error message explains, there is no constructor MouseListener:MouseListener(Window*) or any other constructor with a parameter to which Window* could be implicitly converted.

You probably didn't intend to create a new MouseListener instance, but instead want a unique pointer to this. You can create a unique_ptr to this using the constructor explicit unique_ptr(pointer p):

std::unique_ptr<Window>(this)

This pointer can be passed to MouseManager::addMouseListener. Note that while the conversion from a deriver unique pointer is implicit, a base unique pointer cannot be directly constructed from a bare derived pointer, because the constructor is explicit.


Note that creating a unique pointer to this is probably a dubious idea. It limits the creation of Window objects to using new, and apparently (from the perspective of the code that allocates) leaking the pointer. No other code can own any windows (unless moved from the container where it was initially stored), and you can never have automatic instances.

Upvotes: 0

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275976

make unique makes an object of that type and returns a pointer to it.

It does not wrap a pre existing pointer to an object into a unique ptr.

Your addMouseListener(std::unique_ptr<MouseListener>) function takes ownership of the listener. Passing a preexiting object is not generally a good idea here.

Probably Window should not inherit from MouseListener but instead create a MouseListener that in turn has a pointer to the Window and somehow does lifetime management to ensure the Window alive stays around, or us disconnected when the Window dies.

The MouseListener should relay messages to the window, not be the window.

Upvotes: 1

Related Questions