如何确保辛格尔顿不会被过早销毁
How to ensure Singleton is not destroyed prematurely?
在我的项目中,我正在与大约 4 个单身人士合作,以 Scott Meyer 的方式制作。其中之一:
LevelRenderer& LevelRenderer::Instance()
{
static LevelRenderer obj;
return obj;
}
现在,其中两个单例,LevelRenderer
和LevelSymbolTable
相互交互。例如,在此方法中:
void LevelRenderer::Parse(std::vector<std::string>& lineSet)
{
LevelSymbolTable& table = LevelSymbolTable::Instance();
/** removed code which was irrelevant **/
// for each line in lineSet
BOOST_FOREACH(std::string line, lineSet)
{
// for each character in the line
BOOST_FOREACH(char sym, line)
{
/** code... **/
// otherwise
else
{
sf::Sprite spr;
// Used LevelSymbolTable's Instance here...
table.GenerateSpriteFromSymbol(spr, sym);
// ^ Inside LevelRenderer
/** irrelevant code... **/
}
}
}
}
现在,虽然问题还没有发生。我害怕的是,如果在我调用GenerateSpriteFromSymbol
之前LevelSymbolTable
实例已经被销毁怎么办?
由于我使用了 Scott Meyer 方式,因此单例实例由堆栈分配。因此,保证使用上次创建的第一次销毁规则进行销毁。现在,如果LevelSymbolTable
的实例是在LevelRenderer
的实例之后创建的,它将在LevelRenderer
的实例之前被销毁,对吗?因此,如果我调用一种LevelRenderer
内部LevelSymbolTable
的方法(特别是在LevelRenderer
的析构函数中),我将踩在未定义的行为土地上。
正如我之前所说,这个问题在调试时实际上并没有发生,纯粹是我的假设和猜测。那么,我的结论正确吗?LevelSymbolTable
有可能在LevelRenderer
之前被销毁.如果是这样,有什么办法摆脱这个烂摊子吗?
您不必担心这里的任何事情。 static 关键字保证它从初始化到程序退出时可用。因此,您可以在初始化静态变量后的任何时候调用静态变量。
此外,您还引用了 LevelSymbolTable,而不是局部变量。这就是类名后面的与号的含义。所以你可以在本地使用它,但它实际上是"指代"存在于其他地方的真实对象。因此,当方法退出时,引用将超出范围,但它引用的对象不会。
*好吧,您可能需要担心一件事。在析构函数中,您应该只清理任何内存或文件引用或您有权处理的其他性质的东西。我不知道你为什么要在析构函数中调用其他对象。
定义对象之间的所有权关系。要么有LevelSymbolTable
作为LevelRenderer
的成员:
class LevelRenderer {
LevelSymbolTable symbolTable;
public:
static LevelRenderer& getInstance();
~LevelRenderer() { /* can use symbolTable here */ }
};
或者创建一个同时包含SymbolTable
和Renderer
的单例Level
:
class Level {
SymbolTable symbolTable;
Renderer levelRenderer; // note the order here
public:
static Level& getInstance();
private:
/* have LeverRenderer save reference to symbol table,
now renderer can use symbol table anywhere */
Level() : levelRenderer(symbolTable)
{ /* ... */ }
};
编辑:或者完全摆脱单例。了解为什么单身人士不好。我不知道您的应用程序的结构,但据我所知,您可以Level
为一个知道如何呈现自身并具有其符号表的普通类。并使其生存期连接到它应该在应用程序中表示的级别。
静态实例将在程序开始时(在 main 之前)创建,并在结束时(在 main 之后)进行清理,您不能依赖清理它们的任何特定顺序。也就是说,如果您有两个实例(为了简单起见,让我们将它们设为全局实例)
class one {
one() {}
~one() {}
};
class two {
two() {}
~two() {}
};
one the_one;
two the_other;
int main() {
...
return 0;
}
您不能也不应该假设the_one
在the_other
的构造函数或析构函数中处于活动状态。(反之亦然。
但是,您可以依赖它们在任何其他成员函数中处于活动状态,并且在 main 本身中处于活动状态。
问题中提出的场景不太可能发生,因为Parse
可能在程序仍处于活动状态时被调用。只有当程序即将退出时,才会调用析构函数。
在您的评论中,您指出了一种略有不同的担忧,即全局析构函数相互依赖性。如果您有全局对象向某个全局容器注册自身,则实际上可能会发生这种情况。您可能期望对象会从容器中删除自身,并且容器会弹出对象。
解决此问题的一种方法是允许容器取得向其注册的对象的所有权。这意味着注册到全局容器的是动态分配的实例,而不是 Scott Meyer 的单例实例。然后,全局容器将负责在调用其全局析构函数时清理已注册的项。
- 如何确保C++函数在定义之前声明(如override关键字)
- 如何确保在使用基于布尔值的两个方法之一调用方法时避免分支预测错误
- 使用Unique_ptr确保工厂中的对象唯一
- MESI协议和std::atomic-它是否确保所有写入立即对其他线程可见?
- C++需要帮助从用户那里获得一个整数,并确保它在另外两个整数之间
- 确保流程关闭
- 如何确保接受的C++模板类型使运算符重载?
- 确保编译时的特定 std::array 位置
- C++ 确保子类为常量提供自定义值
- 确保内存映射页位于内存中
- 确保套装新鲜度的有效方法
- C ++类型特征:确保子类实现方法
- 是否确保 2 个连续的 std::chrono::steady_clock::now() 不相等?
- 如何确保用户在一行上提示输入一次时输入名字和姓氏?
- 确保无变体的型号安全
- 确保特征将 AVX 矢量化用于特定操作
- 确保所有构造函数调用相同的函数 c++ 设计模式
- 如何确保 Visual C++ 不允许作用域枚举访问?
- 如何确保调用显式构造函数并允许大括号初始化?
- 确保模拟的 GTest 方法覆盖虚拟方法