C ++ - 防止全局实例化?

时间:2012-11-21 15:01:51

标签: c++ constructor global

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

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

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

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

6 个答案:

答案 0 :(得分:7)

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

然后你可以要求这个类作为你的类的参数,并且初始化总是可以解决。

用于包装器的技术称为RAII,您可以阅读更多相关信息in this SO question和此wiki page。它最初是为了将封装资源初始化和释放组合到对象中,但也可以用于其他各种事物。

答案 1 :(得分: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];

};

我不知道自己是骄傲还是感到羞耻! : - )

答案 2 :(得分: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太糟糕了,你实际上也无法做到?

你敬酒。欢迎文档。

答案 3 :(得分:0)

您可以尝试使用Singleton模式

答案 4 :(得分:0)

  

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

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

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

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

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

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

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

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

答案 5 :(得分:-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

如果您在程序范围内声明它,那么您的对象将只是全局的。