作为序言,我在这里没有完整的最小功能示例来定义工厂,因为我无权访问我正在使用的动态库的定义。
我现在正在寻找的是阅读建议,除非我的问题显然是一般性问题并且不需要了解函数定义的知识来解释。
假设我们有一些工厂模式的变体,根据 DLL 随附的文档,它允许以下内容按预期进行编译和操作:
int main(){
std::cout << "Start" << std::endl;
Base* ptr = Base::Create();
ptr->ConcreteMemberDoSomething();
std::cout << "End" << std::endl;
}
带输出:
Start
/-- Concrete Member... Output --/
End
考虑到这一点,为什么这可能会编译但(始终)导致程序在运行时无限期挂起?:
class Init{
public:
Base* ptr;
Init(){
std::cout << "Ctor start" << std::endl;
ptr = Base::Create();
std::cout << "Ctor end" << std::endl;
};
~Init(){
std::cout << "Dtor" << std::endl;
};
}
Init obj;
int main(){
std::cout << "Start" << std::endl;
obj.ptr->ConcreteMemberDoSomething();
std::cout << "End" << std::endl;
}
带输出:
Ctor start
我希望我的
main()
有更多的调试,但我不明白为什么 Init
构造函数冻结。我担心这与初始化顺序有关,因为我一直在阅读静态初始化顺序问题,但我不知道我可以尝试修复它,因为我无权访问任何在动态库中编译的定义。
这是static initialization order fiasco的可能情况。尽管可能存在其他类型的问题,但此推测仅基于提供的代码片段。一般来说,使用动态初始化创建非函数局部静态对象被认为是糟糕的编程习惯,如果确实不可避免(例如
std::cout
),必须考虑精心设计的临时解决方法。
解决此问题的一种方法称为 Scott Mayer 的 Singleton。用户代码不得存储任何指向单例对象的指针或引用,并且始终使用
singleton_t::instance()
。有些人建议将 Mayer 的单身人士包装在单态类型中:
struct mono{
single* const operator ->() const{
static single ret{/*TODO:init stuff here*/};
return std::addressof(ret);
};
};
//...
{
//In Some code scope:
mono x;
use(x->y);
};
在OP的上下文中,假设存在
destroy
对应的create
我们可以使用unique_ptr
:
struct mono{
auto const operator ->() const{
static std::unique_ptr
<Base,
decltype([](Base *const ptr)
{Base::Destroy(ptr);})>
ret {Base::Create();};
return ret;
};
};
如果这样的
Destroy
存在并且对象根本不应该是delete
d,那么可以使用原始指针代替智能指针。