8

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

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

编辑:该应用程序以嵌入式设备为目标,动态内存分配也被禁止。用户实例化类的唯一可能解决方案是在堆栈上或通过放置新运算符。

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

4

6 回答 6

7

我宁愿通过将 C API 包装到一个类中来安全地调用 C API,而不是对你的类的对象施加一些任意的限制。该类的构造函数将进行初始化,而析构函数将释放获取的资源。

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

用于包装器的技术称为 RAII,您可以在此 SO 问题和此wiki 页面中阅读更多有关它的信息。它原本是为了将资源初始化和释放封装到对象中,但也可以用于各种其他的事情。

于 2012-11-21T15:08:42.900 回答
1

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

因此,让我重新表述这个问题以深入了解其核心:

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

一般来说,响应是:取决于

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

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

例如,我可以创建以下初始化程序

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 如此糟糕,您实际上也不能这样做吗?

你是吐司。欢迎文档。

于 2012-11-21T18:42:23.577 回答
1

半个答案:为了防止堆分配(因此只允许堆栈分配)覆盖 operator 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];

};

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

于 2012-11-21T15:06:06.517 回答
0

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

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

如果全局变量在应用程序进入“main”之前被初始化,那么你可以在“main”设置一些标志之前从构造函数中抛出异常。但是,由实现决定何时初始化全局变量。因此,它们可以在应用程序进入“main”后被初始化。即,这将依赖于未定义的行为,这不是一个好主意。

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

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

所以没有我能想到的好的解决方案。

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

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

于 2012-11-21T17:31:04.317 回答
0

您可以尝试使用单例模式

于 2012-11-21T15:18:27.417 回答
-2

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

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

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

于 2012-11-21T15:13:05.257 回答