C++ 返回标准::唯一\u ptr<;T>;从工厂函数创建纯虚拟接口的完全隐藏实现
我正在阅读boost文档中提供的 在“”一节中,它们提供了一个很好的习惯用法,将实现完全隐藏在纯虚拟接口后面。例如:C++ 返回标准::唯一\u ptr<;T>;从工厂函数创建纯虚拟接口的完全隐藏实现,c++,smart-pointers,c++14,C++,Smart Pointers,C++14,我正在阅读boost文档中提供的 在“”一节中,它们提供了一个很好的习惯用法,将实现完全隐藏在纯虚拟接口后面。例如: // Foo.hpp #include <memory> class Foo { public: virtual void Execute() const = 0; protected: ~Foo() = default; }; std::shared_ptr<const Foo> MakeFoo(); 我的问题是: 你能解释一下为什么
// Foo.hpp
#include <memory>
class Foo {
public:
virtual void Execute() const = 0;
protected:
~Foo() = default;
};
std::shared_ptr<const Foo> MakeFoo();
我的问题是:
~Foo()=default
是否有危险
std::make_shared()
,该对象中的删除程序将直接调用~FooImpl()
:
使用删除表达式或在构造过程中提供给shared_ptr的自定义删除程序销毁对象
创建删除器时,该删除器将被复制到共享\u ptr
在中,删除器是类型的一部分。它是:
模板<
T类,
类Deleter=std::default\u delete
>类唯一性\u ptr;
因此,当您拥有unique\u ptr
时,将直接调用~Foo()
,这是不可能的,因为~Foo()
受保护。这就是为什么当您将Foo()
公开时,它会起作用。工作,如编译。您还必须使其虚拟化
——否则,只需破坏FooImpl
的Foo
部分,就会产生未定义的行为
根据Barry的回答,另一种公开的方法是定义您自己的deleter,该deleter可以访问您的类“
~Foo()
方法”
示例(使用VS2013进行了尝试):
模板
类删除器
{
公众:
void运算符()(T*a)
{
//显式调用a上的析构函数。
a->~a();
}
};
甲级{
好友类deleter;//授予对deleter的访问权。
受保护的:
~A(){
//析构函数。
}
};
std::unique_ptr MakeA()
{
返回std::unique_ptr(新的A());
}
每个shared\u ptr
存储4件东西:指针、强引用计数、弱引用计数和删除器
deleter获取从中构造共享\u ptr
的类型,并删除该类型,而不是公开的类型。如果将其强制转换为基本的共享\u ptr
,则派生的删除程序仍会存储
unique\u ptr
默认情况下不存储这种有状态的删除器
这背后的设计原因是shared_ptr
已经在管理额外的资源:添加deleter是便宜的,因为您已经在管理引用计数
对于unique\u ptr
,如果没有状态删除器,其开销基本上与原始指针相同。默认情况下添加一个有状态的deleter会使惟一性\u ptr
的开销显著增加
虽然它们都是智能指针,unique\u ptr
实际上是最小的,而shared\u ptr
则要复杂得多
您可以通过在unique\u ptr
中附加一个有状态的deleter来解决这个问题
struct stateful_delete {
void const* ptr = nullptr;
void(*f)(void const*) = nullptr;
template<class T>
stateful_delete(T const* t):
ptr(t),
f([](void const* ptr){
delete static_cast<T const*>(ptr);
})
{}
template<class T>
void operator()(T*)const{
if (f) f(ptr);
}
};
template<class T>
using unique_ptr_2 = std::unique_ptr<T, stateful_delete>;
template<class T>
unique_ptr_2<T> unique_wrap_2(T* t) {
return {t, t};
}
template<class T, class...Args>
unique_ptr_2<T> make_unique_2(Args&&...args) {
return unique_wrap( new T(std::forward<Args>(args)...) );
}
struct stateful\u delete{
void const*ptr=nullptr;
无效(*f)(无效常数*)=nullptr;
模板
有状态删除(T常量*T):
ptr(t),
f([](无效常数*ptr){
删除静态广播(ptr);
})
{}
模板
void运算符()(T*)常量{
if(f)f(ptr);
}
};
模板
使用unique_ptr_2=std::unique_ptr;
模板
唯一的\u ptr\u 2唯一的\u包裹\u 2(T*T){
返回{t,t};
}
模板
唯一的\u ptr\u 2使\u唯一的\u 2(Args&&…Args){
返回唯一的包装(新的T(标准::转发(参数)…);
}
这样的unique\u ptr\u 2
是unique\u ptr
的3倍大。它们不进行额外分配(不同于共享\u ptr
)。它们将与非虚拟受保护的~Foo
和公共~FooImpl
一起使用
如果我们使用统一分配的
make_shared
技术,并将ptr
和f
的等价物存储在堆上,则可以将unique_ptr_2
的大小减少到2个指针。我不确定这种复杂性是否值得节省。这在OP的情况下不起作用,因为唯一指针实际上包含一个指向派生的指针,上面的命令将删除基。当然,我错过了非虚拟基析构函数。
std::unique_ptr<const Foo> MakeUniqueFoo() {
return std::make_unique<const FooImp>();
}
template<
class T,
class Deleter = std::default_delete<T>
> class unique_ptr;
template <typename T>
class deleter
{
public:
void operator()(T* a)
{
// Explicitly call the destructor on a.
a->~A();
}
};
class A {
friend class deleter<A>; // Grant access to the deleter.
protected:
~A() {
// Destructor.
}
};
std::unique_ptr<A, deleter<A>> MakeA()
{
return std::unique_ptr<A, deleter<A>>(new A());
}
struct stateful_delete {
void const* ptr = nullptr;
void(*f)(void const*) = nullptr;
template<class T>
stateful_delete(T const* t):
ptr(t),
f([](void const* ptr){
delete static_cast<T const*>(ptr);
})
{}
template<class T>
void operator()(T*)const{
if (f) f(ptr);
}
};
template<class T>
using unique_ptr_2 = std::unique_ptr<T, stateful_delete>;
template<class T>
unique_ptr_2<T> unique_wrap_2(T* t) {
return {t, t};
}
template<class T, class...Args>
unique_ptr_2<T> make_unique_2(Args&&...args) {
return unique_wrap( new T(std::forward<Args>(args)...) );
}