C++ 与自定义分配器一起使用的最佳唯一指针是什么?
我正在使用一个自定义分配器,它需要知道删除时对象的真实类型*它还应该取消分配与从分配函数接收到的地址完全相同的地址。这是我第一个使用任何特殊分配器的项目,所以我对最佳实践没有经验或知识。我希望本质上具有与唯一指针相同的语义。下面是显示问题的明显失败尝试:C++ 与自定义分配器一起使用的最佳唯一指针是什么?,c++,pointers,c++11,unique-ptr,allocator,C++,Pointers,C++11,Unique Ptr,Allocator,我正在使用一个自定义分配器,它需要知道删除时对象的真实类型*它还应该取消分配与从分配函数接收到的地址完全相同的地址。这是我第一个使用任何特殊分配器的项目,所以我对最佳实践没有经验或知识。我希望本质上具有与唯一指针相同的语义。下面是显示问题的明显失败尝试: template<typename T> void* allocate(){ return malloc(sizeof(T)); //I really do something else but its beyond the
template<typename T>
void* allocate(){
return malloc(sizeof(T)); //I really do something else but its beyond the scope of this example
}
template<typename T>
void deallocate(T * p){
// T must be real T and not base of T
free(p); //I really do something else but its beyond the scope of this example
}
std::unique_ptr<Base> pb(new Base);
std::unique_ptr<Derived> pd(new Derived);
std::unique_ptr<Base> pb2(std::move(pd));
//std::unique_ptr<Derived> pd2(std::move(pb)); this should not compile
//pb.swap(pd); this should not compile
pb.swap(pb2);
这会失败,因为unique_ptr需要调用my deallocate而不是delete
在这种情况下,最好使用什么指针
更新:
添加一个带类型擦除的有状态古装删除程序可能会解决我的问题。下面是一个std::函数的示例
template<typename T>
using UniquePtr = std::unique_ptr<T,std::function<void(T*)>>;
//using my special unique_ptr
UniquePtr<Base> upb(new (allocate<Derived>()) Derived,[](Base*p){
if(p!=nullptr){
auto d = static_cast<Derived*>(p);
d->~Derived();
deallocate(d);
}
});
我想不出另一种方法,在将实际派生类转换为基然后四处移动的情况下,让它的知识跟随指针。不过,这似乎非常冗长且容易出错。这是最好的方式吗
编辑2:
添加factory函数可以解决一些问题,并减少出错的可能性:
template<typename T_Base, typename T_Derived, typename... Ts>
std::unique_ptr<T_Base,std::function<void(T_Base* p)>> MakeUnique(Ts &&... Args){
return std::unique_ptr<T_Base,std::function<void(T_Base*)>>(
new (allocate<T_Derived>()) T_Derived(std::forward<Ts>(Args)...),
[](T_Base*p){
if(p!=nullptr){
auto d = static_cast<T_Derived*>(p);
d->~T_Derived();
deallocate(d);
}
});
}
//example use
auto mupd = MakeUnique<Base,Derived>();
auto mupdd = MakeUnique<Derived,Derived>();
auto mupb = MakeUnique<Base,Derived>();
auto mupbb = MakeUnique<Base,Base>();
mupbb.swap(mupb);
mupb = std::move(mupd);
//mupb = std::move(mupdd); should give error
这是开始觉得有用,所以似乎我可能已经解决了我自己的问题。对于任何提出批评或想法的人,我仍然非常感激
*我原本不清楚这个要求 不确定这是否是您要找的。但是,通过使用函数指针作为具有唯一\u ptr的删除器,可以获得动态删除器的许多功能:
不确定这是否是你要找的。但是,通过使用函数指针作为具有唯一\u ptr的删除器,可以获得动态删除器的许多功能:
从您所展示的代码中,绝对没有理由知道派生类型:如果将基本析构函数设置为虚拟并调用它,它也会自动调用派生析构函数。对派生对象调用特定方法的任何其他操作都是设计错误,应该在派生类的析构函数中处理。@syam我知道需要虚拟析构函数,但是,在这种特定情况下,必须使用与allocate相同的类型调用我的deallocate函数。这需要一段时间来解释为什么会这样,让我们说这是一个要求。@当已经需要自定义分配器时,syam使客户机类多态通常不是一个可行的快速修复方法。从您所展示的代码中,绝对没有理由知道派生类型:只需将基本析构函数设置为虚拟并调用它,它也会自动调用派生析构函数。对派生对象调用特定方法的任何其他操作都是设计错误,应该在派生类的析构函数中处理。@syam我知道需要虚拟析构函数,但是,在这种特定情况下,必须使用与allocate相同的类型调用我的deallocate函数。我们需要一段时间来理解为什么会这样,我们就说这是一个要求。@当已经需要自定义分配器时,syam使客户端类多态通常不是一个可行的快速修复方法。是的,当然一个简单的函数指针比一个std::函数小得多,它满足了所有的要求。我想我说得太快了,由于vtable或多重继承的原因,当指向派生的指针和指向基址的指针不在同一地址时,这将立即中断,因为static_caststatic_castp并不总是等于p。看到了吗,这解决了问题,但灵活性较差,我想我会使用这种方法,但会添加一个工厂,这样用户就不会轻易破坏它。是的,当然,一个简单的函数指针比一个std::函数小得多,它满足了所有的要求。我想我说得太早了,由于vtable或多重继承的原因,当指向派生的指针和指向基址的指针不在同一地址时,这将立即中断,因为static_caststatic_castp并不总是等于p。看到这个解决了问题,但灵活性较差,我想我会使用这个方法,但会添加一个工厂,这样用户就不会轻易破坏它。
std::unique_ptr<Base, void(*)(void*)> p(allocate<Derived>(), deallocate<Derived>);
#include <memory>
#include <new>
#include <cstdlib>
template<typename T>
T*
allocate()
{
std::unique_ptr<T, void(*)(void*)> hold(static_cast<T*>(std::malloc(sizeof(T))),
std::free);
::new (hold.get()) T;
return static_cast<T*>(hold.release());
}
template<typename T>
void
deallocate(void* p)
{
static_cast<T*>(p)->~T();
std::free(p);
}
#include <iostream>
struct Base
{
Base() {std::cout << "Base()\n";}
Base(const Base&) = delete;
Base& operator=(const Base&) = delete;
~Base() {std::cout << "~Base()\n";}
virtual void bark() const {std::cout << "Hi Base!\n";}
};
struct Derived
: public Base
{
Derived() {std::cout << "Derived()\n";}
Derived(const Base&) = delete;
Derived& operator=(const Derived&) = delete;
~Derived() {std::cout << "~Derived()\n";}
void bark() const {std::cout << "Hi Derived!\n";}
};
int
main()
{
std::unique_ptr<Derived, void(*)(void*)> p(allocate<Derived>(), deallocate<Derived>);
p->bark();
std::unique_ptr<Base, void(*)(void*)> p2 = std::move(p);
p2->bark();
}
Base()
Derived()
Hi Derived!
Hi Derived!
~Derived()
~Base()