内存分配器范围的设计

4
我一直在阅读有关内存管理的不同文章,为了准备我的架构工作,我最担心的是分配器将如何在整个代码库中使用、创建和处理。其中一个问题是,我的设计总是将分配器放在全局范围内,因为我没有一个典型的单例设计来包含分配器,它们没有真正的位置可以存放。我想避免使用全局变量,因为使用全局变量通常会出现所有问题。

这导致我设计了类似以下的东西:

void* operator new(size_t size, uint32_t type)
{
    return gAllocator.Alloc(size, type);
 }

这将导致在头文件中仅有新定义,而在.cpp中有声明。这个.cpp文件将只包含 gAllocator,且只在.cpp文件中可访问 (除了new调用之外的任何地方)。
如果我的设计是这样的,那么gAllocator是否仍然是全局变量?如果不是,它将被认为是什么类型的变量?如果它只存在于命名空间的范围内呢?

为什么不为默认分配器实例创建一个典型的单例? - Potatoswatter
2
Potatoswatter:单例通常是糟糕的设计,特别是在游戏开发中(我的兴趣所在)。 - chadb
1
从技术上讲,如果您允许默认值之外的其他人,则不是真正的单例模式。只需遵循单例模式以创建一个安全初始化的全局实例即可。 - Potatoswatter
1个回答

1

看起来一个简单的全局变量就是你想要的。回顾一下,在C++中,全局(或单例)应该是在一个inline函数中的本地static变量。

class myAllocator {
public:
    static myAllocator &getDefaultInstance() {
        static myAllocator theInstance( parameters );
        return theInstance;
    }
};

这种方式可以在第一次使用对象时进行初始化。如果您使用典型的头文件声明+.cpp定义,那么与其他全局变量相比的初始化顺序是未定义的,并可能导致不可预测的后果。(“静态初始化顺序混乱”)


@CharlesSalvia 是的,是这样的。实现需要在初始化周围添加互斥锁! - Potatoswatter
全局对象比这种单例更简单、更健壮。 - Maxim Egorushkin
@MaximYegorushkin Schwarz计数器只是手动实现了这个习语隐式执行的内容。但您还必须手动添加线程安全性,并且(至关重要的是)该类需要围绕构造体实现,这意味着构造函数必须为空...初始化的委托会导致问题。如果您想避免函数调用语法,则可以实现一个对象,该对象通过返回全局实例而隐式转换为“myAllocator”类型。 - Potatoswatter
@Potatoswatter:C++动态初始化只有一个线程在执行,因此在使用Schwarz计数器时无需使初始化线程安全。该类需要围绕构造实现,这意味着构造函数必须为空 - 不一定,再看看std::cout如何初始化。 - Maxim Egorushkin
...全局变量的初始化会产生一个线程,该线程引用另一个全局变量。典型的Schwarz计数器不适用于这种情况——恰恰相反。使用Schwarz计数器可以确保另一个全局对象在另一个全局对象引用它的时候已经被初始化。 - Maxim Egorushkin
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接