簡體   English   中英

推遲C ++靜態對象構建 - Linux上的GCC

[英]Deferring C++ static object construction - GCC on Linux

想象一下,我有一個名為MyClass的C ++類。

想象一下,我無法訪問MyClass的源代碼......它包含在一個庫中,我只提供了庫和MyClass的頭文件。

想象一下,類本身需要環境預配置...例如......在可以調用類的構造函數之前,我需要做一些設置。 該類通常用於如下:

void func() {
   doGlobalSetup();
   MyClass myInstance(1,2,3);
   myInstance.doSomething();
   ...
}

現在我遇到了需要創建類的全局實例的情況,例如:

MyClass myInstance(1,2,3);

int main(int argc, char *argv[]) {
   doGlobalSetup();
   myInstance.doSomething();
}

問題是在這個故事中, MyClass的實例是調用doGlobalSetup() 之前創建的。 它在調用main()之前被實例化。 我想要做的是將myInstance()的創建推遲到以后, 或者能夠在類的實例化之前以某種方式運行doGlobalSetup()

這是對實際故事的簡化......所以讓我們假設:

  1. 我無法改變MyClass的內部。
  2. 必須有一個名為myInstanceMyClass類型的實例變量(我無法將邏輯更改為MyClass *pMyInstance )。

非常感謝閱讀。

因為你已經限制了這個問題,所以無法使用new 您應該能夠始終創建對象並將其復制到全局實例。 例如:

MyClass createMyClass()
{
    doGlobalSetup();
    return MyClass(1, 2, 3);
}

MyClass myInstance = createMyClass();

int main()
{
    myInstance.doSomething();

    return 0;
}

它適合您的需求嗎?

namespace
{
    int doStaticGlobalSetup()
    {
        doGlobalSetup();
        return 0;
    }
}
MyClass myInstance(doStaticGlobalSetup() + 1,2,3);

int main() {
   myInstance.doSomething();
   return 0;
}

如果你必須推遲任何構造函數調用,直到全局初始化完成,並且想要確保沒有發生靜態順序初始化失敗,有一種方法:使myInstance引用未初始化的內存塊並使用放置在其中創建對象全球初始化之后的新事物。

#include <iostream>
#include <type_traits>

struct foo
{
    foo() { std::cout << "created\n"; }
    void meow() { std::cout << "used\n"; }
    ~foo() { std::cout << "destroyed\n"; }
};
void doGlobalSetup() { std::cout << "Global setup\n"; }


//Actual implementation
namespace {
    typename std::aligned_storage<sizeof(foo), alignof(foo)>::type bar;
}
foo& instance = reinterpret_cast<foo&>(bar);

//Allows automatic creation and destruction
struct initializer
{
    initializer()
    {
        if (!initialized)
            new (&instance) foo();
        initialized = true;
    }
    ~initializer()
    {
        if(initialized)
            instance.~foo();
        initialized = false;
    }
    private:
        static bool initialized;
};
bool initializer::initialized = false;

int main()
{
    doGlobalSetup();
    initializer _;
    instance.meow();
}

在函數內使用靜態變量。

MyClass &myInstance() {
   doGlobalSetup();
   static MyClass myInstance(1,2,3);
   return myInstance;
}

void func() {
   myInstance().doSomething();
}

你可能已經得到了你想要的答案。 但只是為了涵蓋整個范圍:如果由於某種原因,您希望確保代碼中的其他位置不會意外地構建MyClass而不依賴於您的全局變量 - 並且在進行全局設置之前 - 您需要用鏈接解決這個問題。

如果你在Linux上,你可以LD_PRELOAD一個只包含MyClass構造函數符號的共享對象。 在其中,您相應地聲明了setup函數,並讓動態鏈接器為您完成工作。 然后,在構造函數內部,調用setup函數,然后執行dlsym("...", RTLD_NEXT)以獲取指向原始構造函數的指針,並調用它,並將所傳遞的參數傳遞給它。 當然,您維護並檢查靜態標志是否已執行設置。

再說一遍,這對你來說可能有點過頭了,但我發布它是為了以防有人需要(並且能夠使用)這種解決方案。

PS這是你依賴全球狀態時得到的! :)

首先,請記住,給定一個類似doGlobalSetup的庫初始化函數,如果創建一個全局實例,那么庫就會有一個明顯的非零機會。

否則,使用逗號運算符創建初始化程序非常容易:

bool do_my_setup = (doGlobalSetup(), true);
MyClass myInstance(1,2,3);

在GCC編譯器環境中,有一個名為constructor的函數屬性功能。 這允許我們標記一個函數定義,它能夠在調用main 之前自動調用它,最重要的是調用任何類構造函數之前

返回原始問題定義...如果修改了doGlobalSetup()函數:

void doGlobalSetup() { ... }

__attribute__((constructor)) void doGlobalSetup() { ... }

那么它的調用之前主要將發生被稱為,被稱為任何靜態類的實例構造函數之前 由於其工作已隱式執行,因此也將從main()中刪除對此函數的顯式調用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM