C++-防止全局实例化

C++ - Prevent global instantiation?

本文关键字:实例化 全局 C++-      更新时间:2023-10-16

有没有办法强制类在堆栈上实例化,或者至少防止它在C++中是全局的?

我想防止全局实例化,因为构造函数调用需要先前初始化的C API。AFAIK没有办法控制全局对象的构造顺序。

编辑:应用程序针对的是一个嵌入式设备,该设备也禁止动态内存分配。用户安装类的唯一可能的解决方案是在堆栈上或通过placement new运算符。

第2版:我的类是一个依赖于其他外部库(来自C API)的库的一部分。我无法修改这些库,也无法控制在最终应用程序中初始化库的方式,这就是为什么我正在寻找一种方法来限制类的使用方式。

与其对类的对象设置一些任意的限制,我更愿意通过将C API的调用封装到一个类中来确保调用的安全性。该类的构造函数将进行初始化,析构函数将释放获取的资源。

然后,您可以要求这个类作为类的参数,初始化总是会成功的。

用于包装的技术被称为RAII,您可以在这个SO问题和这个wiki页面中阅读更多关于它的信息。它最初的目的是将封装资源初始化和发布结合到对象中,但也可以用于各种其他事情。

半个答案:为了防止堆分配(所以只允许堆栈分配),重写操作符new并使其私有。

void* operator new( size_t size );

编辑:其他人说只记录限制,我有点同意,尽管如此,我还是同意:没有堆分配,没有全局分配,API初始化(不完全在构造函数中,但我认为仍然足够好):

class Boogy
{
public:
static Boogy* GetBoogy()
{
// here, we intilialise the APIs before calling
// DoAPIStuffThatRequiresInitialisationFirst()
InitAPIs();
Boogy* ptr = new Boogy();
ptr->DoAPIStuffThatRequiresInitialisationFirst();
return ptr;
}
// a public operator delete, so people can "delete" what we give them
void operator delete( void* ptr )
{
// this function needs to manage marking array objects as allocated                        
// or not
}
private:
// operator new returns STACK allocated objects.  
void* operator new( size_t size )
{
Boogy* ptr = &(m_Memory[0]);
// (this function also needs to manage marking objects as allocated 
// or not)
return ptr;
}
void DoAPIStuffThatRequiresInitialisationFirst()
{
// move the stuff that requires initiaisation first
// from the ctor into HERE.
}
// Declare ALL ctors private so no uncontrolled allocation, 
// on stack or HEAP, GLOBAL or otherwise, 
Boogy(){}
// All Boogys are on the STACK.
static Boogy m_Memory[10];
};

我不知道我是骄傲还是羞愧!:-)

您本身不能阻止将对象作为全局变量。我认为你不应该尝试:毕竟,为什么不能构建一个初始化这些库的对象,全局实例化它,然后全局实例化你的对象?

所以,让我重新表述这个问题,深入到它的核心:

在完成一些初始化工作之前,如何防止构建我的对象?

通常情况下,响应为:取决于

这一切都归结为初始化工作,特别是:

  • 有没有办法检测它还没有被调用
  • 多次调用初始化函数有缺点吗

例如,我可以创建以下初始值设定项

class Initializer {
public:
Initializer() { static bool _ = Init(); (void)_; }
protected:
// boilerplate to prevent slicing
Initializer(Initializer&&) = default;
Initializer(Initializer const&) = default;
Initializer& operator=(Initializer) = default;
private:
static bool Init();
}; // class Initializer

这个类第一次实例化时,它调用Init,然后忽略它(以微不足道的比较为代价)。现在,从这个类(私下)继承以确保在调用构造函数的初始值设定项列表或主体时,已经执行了所需的初始化,这很简单。

应该如何实现Init

取决于什么是可能的和更便宜的,要么检测初始化已完成,要么调用初始化。

如果C API太糟糕了,你实际上也不能这样做?

你完蛋了。欢迎文档。

您可以尝试使用Singleton模式

有没有办法强制类在堆栈上实例化,或者至少防止它在C++中是全局的?

不是您可以将构造函数设为私有,只使用工厂方法创建所述对象,但没有什么能真正阻止您使用所述方法创建全局变量。

若全局变量是在应用程序进入"main"之前初始化的,那个么您可以在"main"设置一些标志之前从构造函数抛出异常。然而,何时初始化全局变量取决于实现。所以,它们可以在应用程序进入"main"后进行初始化。也就是说,这将依赖于未定义的行为,这不是一个好主意。

理论上,您可以尝试遍历调用堆栈,并从中查看它是被调用的。然而,编译器可以内联构造函数或几个函数,这将是不可移植的,并且在C++中遍历调用堆栈将是痛苦的。

您也可以手动检查"this"指针,并尝试猜测它的位置。然而,这将是特定于该特定编译器、操作系统和体系结构的不可移植的黑客攻击。

所以我想不出什么好的解决办法。

因此,最好的想法是改变你的程序行为,正如其他人已经建议的那样——创建一个单例类,在构造函数中初始化你的C api,在析构函数中解初始化它,并在必要时通过工厂方法请求这个类。这将是解决你的问题最优雅的方法。

或者,您可以尝试记录程序行为。

要在堆栈上分配一个类,只需说

FooClass foo; // NOTE no parenthesis because it'd be parsed 
// as a function declaration. It's a famous gotcha.

要在堆上进行分配,您可以说

std::unique_ptr<FooClass> foo(new FooClass()); //or
FooClass* foop = new FooClass(); // less safe

只有在程序范围内声明对象时,对象才会是全局的。