我正在使用std::unique_ptr丘疹成语:
class window {
window(const rectangle& rect);
private:
class window_impl; // defined elsewhere
std::unique_ptr<window_impl> impl_; // won't compile
};
然而,我得到了一个关于使用不完整类型的编译错误,在<memory>中的304行:
对不完整类型uixx::window::window_impl的sizeof应用无效
据我所知,std::unique_ptr应该能够与不完整类型一起使用。这是一个错误在libc++或我在这里做错了什么?
可能在类的.h文件中有一些使用不完全类型的函数体。
确保在.h for class窗口中只有函数声明。window的所有函数体必须在.cpp文件中。对于window_impl也是如此…
顺便说一句,你必须在你的.h文件中显式地为windows类添加析构函数声明。
但是你不能在头文件中放空的dtor主体:
class window {
virtual ~window() {};
}
肯定只是个宣言:
class window {
virtual ~window();
}
可能在类的.h文件中有一些使用不完全类型的函数体。
确保在.h for class窗口中只有函数声明。window的所有函数体必须在.cpp文件中。对于window_impl也是如此…
顺便说一句,你必须在你的.h文件中显式地为windows类添加析构函数声明。
但是你不能在头文件中放空的dtor主体:
class window {
virtual ~window() {};
}
肯定只是个宣言:
class window {
virtual ~window();
}
使用自定义删除器
问题是unique_ptr<T>必须在它自己的析构函数、它的move赋值操作符和unique_ptr::reset()成员函数中调用析构函数T::~T()。但是,在几种PIMPL情况下(已经在外部类的析构函数和move赋值操作符中)必须调用这些函数(隐式或显式)。
正如在另一个回答中已经指出的,避免这种情况的一种方法是将所有需要unique_ptr::~unique_ptr()、unique_ptr::operator=(unique_ptr&&)和unique_ptr::reset()的操作移动到实际定义pimpl helper类的源文件中。
然而,这是相当不方便的,在某种程度上违背了皮条客习俗的要点。一个更干净的解决方案是使用一个自定义删除器,只将它的定义移动到粉刺助手类所在的源文件中。这里有一个简单的例子:
// file.h
class foo
{
struct pimpl;
struct pimpl_deleter { void operator()(pimpl*) const; };
std::unique_ptr<pimpl,pimpl_deleter> m_pimpl;
public:
foo(some data);
foo(foo&&) = default; // no need to define this in file.cc
foo&operator=(foo&&) = default; // no need to define this in file.cc
//foo::~foo() auto-generated: no need to define this in file.cc
};
// file.cc
struct foo::pimpl
{
// lots of complicated code
};
void foo::pimpl_deleter::operator()(foo::pimpl*ptr) const { delete ptr; }
除了单独的delete类,你也可以使用foo的free函数或static成员:
class foo {
struct pimpl;
static void delete_pimpl(pimpl*);
using deleter = void(&)(pimpl*);
std::unique_ptr<pimpl,deleter> m_pimpl;
public:
foo(some data);
};
下面是一些带有不完整类型的std::unique_ptr的例子。问题在于破坏。
如果你在unique_ptr中使用pimpl,你需要声明一个析构函数:
class foo
{
class impl;
std::unique_ptr<impl> impl_;
public:
foo(); // You may need a def. constructor to be defined elsewhere
~foo(); // Implement (with {}, or with = default;) where impl is complete
};
因为否则编译器会生成一个默认值,它需要为此完整地声明foo::impl。
如果你有模板构造函数,那么你就完蛋了,即使你不构造impl_成员:
template <typename T>
foo::foo(T bar)
{
// Here the compiler needs to know how to
// destroy impl_ in case an exception is
// thrown !
}
在命名空间范围内,使用unique_ptr也不能工作:
class impl;
std::unique_ptr<impl> impl_;
因为编译器必须知道如何销毁这个静态持续时间对象。解决方法是:
class impl;
struct ptr_impl : std::unique_ptr<impl>
{
~ptr_impl(); // Implement (empty body) elsewhere
} impl_;