Reputation: 1663
Assume I have a templated MemoryPool class a function create(...) (which returns a pointer to a newly allocated object of type T) and a function destroy(T*) (which destroys and returns the memory back to the pool).
I would like to create a std::unique_ptr that "owns" the pointer created by the pool and returns the pointer to the pool, thus requiring a custom deleter.
The problem is, how do I make this work if the pool contains concrete objects and I want to pass around a std::unique_ptr to an abstract interface of this object.
Here is an example that doesn't compile:
#include <iostream>
#include <memory>
#include <functional>
#include <utility>
template <typename T>
class MemoryPool {
public:
template <typename ... ARGS>
T* create(ARGS&&... args) {
std::cout << "MemoryPool::create()" << std::endl;
return new T(std::forward<ARGS>(args)...);
}
void destroy(T* ptr) {
std::cout << "MemoryPool::destroy()" << std::endl;
delete ptr;
}
};
class ITest {
public:
ITest() {
std::cout << "ITest::ITest()" << std::endl;
}
virtual ~ITest() {
std::cout << "ITest::~ITest()" << std::endl;
}
virtual void sayHello() = 0;
};
class Test :public ITest {
public:
Test() {
std::cout << "Test::Test()" << std::endl;
}
~Test() {
std::cout << "Test::~Test()" << std::endl;
}
void sayHello() override {
std::cout << "Test says hello" << std::endl;
}
};
class ITestOwner {
public:
ITestOwner(std::unique_ptr<ITest> ptr) :
_ptr(std::move(ptr))
{
std::cout << "ITestOwner::ITestOwner()" << std::endl;
}
~ITestOwner() {
std::cout << "ITestOwner::~ITestOwner()" << std::endl;
}
void sayHello() { _ptr->sayHello(); }
private:
std::unique_ptr<ITest> _ptr;
};
int main() {
MemoryPool<Test> pool;
std::unique_ptr<Test, std::function<void(Test*)>> ptr(pool.create(), [&pool](Test* ptr){
std::cout << "Custom Deleter" << std::endl;
pool.destroy(ptr);
});
ITestOwner owner(std::move(ptr));
owner.sayHello();
return 0;
}
Keep in mind that, my real MemoryPool class would actually act as a normal memory pool and not use new/delete as I have done.
In this example, ITestOwner should take over ownership of the std::unique_ptr to a ITest abstract object. Then, when ITestOwner is destroyed, the smart pointer will be destroyed, and the Test object should be returned to the memory pool.
Is there a way to accomplish this?
Upvotes: 3
Views: 416
Reputation: 4713
The code doesn't compile because std::unique_ptr<ITest>
is used by ITestOwner
while you to forward it std::unique_ptr<Test, std::function<void(Test*)>>
. It obviously doesn't compile because std::unique_ptr<ITest>
calls delete on ITest
instead of calling some complex arbitrary function.
You'd need to use unique_ptr<ITest, function<void(ITest*)>>
for it to work and in addition add some unsighty conversion code from function<void(Test*)>
to function<void(ITest*)>
... I'd say this is simply not good. unique_ptr
is designed to be simple and efficient - the destructor is supposed to wrap basic functionality but it isn't convenient enough for complicated purposes.
Basically, unique_ptr
is not designed for this task. It is supposed to be lightweight and you already use heavy functionality like std::function
that ruins the whole purpose. Instead, you can use shared_ptr
which type erases the deleter and hides it - so you'd need nothing to worry about. If you want to still restrict user to unique ownership you can surely find other 3rd party open source libraries that implement the smart pointer you want - there are lots of them.
Upvotes: -1