【发布时间】:2025-12-29 07:05:06
【问题描述】:
我正在编写一个使用两阶段初始化的C++17 GUI library。它位于本机 GUI API(目前:Win32)之上,并向用户隐藏其复杂性。
将 Windows 映射到 C++ 类通常需要两个阶段初始化:第一阶段创建 C++ 对象,第二阶段创建 API 对象。
这是必需的,因为订阅窗口事件的第一个机会是在构造函数中。因此,如果您的基本构造函数在您订阅事件之前已经创建了窗口,那么您会错过一些。除此之外,不应该从 C++ 中的构造函数调用虚函数,所以没有办法绕过它。
我想使用以下模式来确保 Windows 资源(窗口、GDI 句柄等)的两阶段初始化:
所有使用资源的函数都有 create() 和 destroy() 成员函数,从基模板类派生。
template <class T>
class resource {
public:
virtual T* create()=0;
virtual void destroy()=0;
}
然后我可以从中派生类:
class wnd : resource<wnd> {
// do the magic
}
要创建这些类,我想创建一个全局库函数,它可以执行以下操作:
template <class T, typename... A>
// TODO: Need a concept here, but not available yet.
static std::unique_ptr<T> create(A... args)
{
T* ptr = new T(args...);
ptr->create();
return std::unique_ptr<T>(ptr);
}
所以我将其称为auto button=::create<button>(ctor args),然后它会将参数传递给 ctor,将 unique_ptr 返回给我的按钮并在其上调用 create 函数。
现在我也想实施销毁。它将作为自定义删除器附加到 unique_ptr 并调用 destroy()。这就是我 - 理论上 - 认为它应该工作的方式。
// --- two phase construction pattern ---
template <typename T>
struct destroy {
void operator()(T* p) { p->destroy(); delete p; }
};
template <class T, typename... A>
static std::unique_ptr<T> create(A... args)
{
T* ptr = new T(args...);
ptr->create();
return std::unique_ptr<T, destroy<T>>(ptr);
}
问题在于这会失败,因为 std::unique_ptr
我想过在析构函数中调用destroy,但后来我读到构造函数和析构函数应该避免调用虚函数,所以我必须在每个析构函数中重新实现它。库用户可能会在派生类中忘记这一点并造成内存泄漏。
这个问题有更好的解决方案吗?
【问题讨论】:
标签: c++ initialization unique-ptr