静的ライブラリにおけるグローバル(静的)変数の初期化の問題

4198 ワード

C++グローバル変数のコンストラクション関数とコンストラクション関数は、main呼び出し前の初期化作業とmain呼び出し後のクリーンアップ作業を実行します.このテクニックを使用すると、コードをより簡潔にすることができますが、このテクニックは複雑です.このテクニックを使用するファクトリモードと、静的ライブラリで使用する問題について説明します.
       ベースクラスTestClassには、TestClassの各サブクラスを作成できる作成関数Createが定義されています.基本クラスメンテナンスサブクラスの作成関数リストを実装し、Createのパラメータparamに基づいて作成関数リストから適切な関数を選択してサブクラスを作成します.
// test_class.h
#ifndef TEST_CLASS_H
#define TEST_CLASS_H

#include 

class TestClass {
protected:
    TestClass(int id) { m_Id = id; }

public:
    int GetId() const {
        return m_Id;
    }
    
protected:
    int m_Id;

public:
    typedef TestClass* (*CreateFcn)(int param);

    static TestClass* Create(int param) {
        TestClass* retVal = NULL;
        for (std::list::const_iterator iter = GetCreaterList().begin();
            iter != GetCreaterList().end(); ++iter) {
            retVal = (*iter)(param);
            if (retVal != NULL) break;
        }
        return retVal;
    }

    class CreaterAdder {
    public:
        CreaterAdder(CreateFcn c) {
            TestClass::GetCreaterList().push_front(c);
        }
    };

    static std::list& GetCreaterList() {
        static std::list s_CreaterList;
        return s_CreaterList;
    }
    
};

#endif // TEST_CLASS_H

TestClass::CreaterAdder
作成関数を追加します.
GetCreaterList
関数の役割はグローバル変数のもう一つの問題を解決し,初期化の順序問題は,本論文では詳しく述べない.
        TestClassのサブクラスTestSubClassAは独自の作成関数Createを定義し、cppファイルでグローバル静的変数adderを定義し、adderのコンストラクション関数はTestClassの作成関数リストに作成関数を追加します.
// test_sub_class_a.h
#ifndef TEST_SUB_CLASS_A_H
#define TEST_SUB_CLASS_A_H

#include "test_class.h"

class TestSubClassA : public TestClass {
protected:
    TestSubClassA() : TestClass('a') {}
    
public:
    static TestSubClassA* Create(int param) {
        if (param == 'a') {
            return new TestSubClassA;
        } else {
            return NULL;
        }
    }
    
};

#endif // TEST_SUB_CLASS_A_H
// test_sub_class_a.cpp
#include "test_sub_class_a.h"

static TestClass::CreaterAdder adder((TestClass::CreateFcn)TestSubClassA::Create);

        test_main.cppは、作成関数を使用する場合に表示されます.
#include 
#include "test_class.h"

void Test_TestClassCreate(int id) {
    printf("  %c  :", id);
    
    TestClass* tc = TestClass::Create(id);
    
    if (tc != NULL) {
        printf("  ,id %c
", tc->GetId()); delete tc; } else { printf("
"); } } int main() { Test_TestClassCreate('a'); Test_TestClassCreate('b'); Test_TestClassCreate('c'); return 0; }

コンパイルリンクは次のように実行されます.
aインスタンスの作成:成功、idはa
bインスタンスの作成:失敗
cインスタンスの作成:失敗
        上記のコードから、TestClass::Createはサブクラス情報を知る必要がなく、サブクラスを作成し、グローバル静的変数を利用して新しいサブクラスを追加し、既存のファイルを変更する必要がありません.TestSubClassAのパターンに従ってTestSubClassBを定義できます.
// test_sub_class_b.h
#ifndef TEST_SUB_CLASS_B_H
#define TEST_SUB_CLASS_B_H

#include "test_class.h"

class TestSubClassB : public TestClass {
protected:
    TestSubClassB() : TestClass('b') {}

public: 
    static TestSubClassB* Create(int param) {
        if (param == 'b') {
            return new TestSubClassB;
        } else {
            return NULL;
        }
    }
    
};

#endif // TEST_SUB_CLASS_B_H
// test_sub_class_b.cpp
#include "test_sub_class_b.h"

static TestClass::CreaterAdder adder((TestClass::CreateFcn)TestSubClassB::Create);
コンパイルリンクは次のように実行されます.
aインスタンスの作成:失敗、idはa
bインスタンスの作成:失敗、idはb
cインスタンスの作成:失敗
        先にtest_をsub_class_a.cppとtest_sub_class_b.cppを静的ライブラリにコンパイルしtest_とmain.cppリンクは、問題を使用します.実行結果は次のとおりです.
aインスタンスの作成:失敗
bインスタンスの作成:失敗
cインスタンスの作成:失敗
表示結果から見るとtest_はありませんsub_class_aとtest_sub_class_bの作成関数.
        問題の原因は、静的ライブラリをリンクすると、リンクは静的ライブラリの変数と関数が使用されているかどうかをチェックし、使用されていないものはリンクされません.コードから見るtest_sub_class_a.cppとtest_sub_class_b.cppのadderは外部で使用されていないため、リンクも構造関数も呼び出されません.現在、上記の問題を解決する方法は、adderを初期化関数に定義し、その関数を明示的に呼び出すことである.