変数の初期化・登録に静的変数を使っても大丈夫でしょうか?

StackOverflow https://stackoverflow.com/questions/2337446

  •  22-09-2019
  •  | 
  •  

質問

言語:C ++ツールキット:Qt4

私が使用しているツールキットには、と呼ばれる静的メソッドがあります int QEvent::registerEventType() 独自のイベント タイプを登録します。これをサブクラス化すると QEvent この値を基本クラスに提供する必要があります。 QEvent::QEvent(int type).

アプリケーションの起動前に静的変数を使用してこれを呼び出しても問題ありませんか?次のことを考慮してください。

//This is all in my .cpp file

static int myEventType;  //This will contain my registered type

/*If I create a static instance of this class the constructor 
  gets called before the main() function starts.
*/
class DoRegisterMyEventType {  
public:
  DoRegisterMyEventType() {
    myEventType = QEvent::registerEventType();
  }
};

static DoRegisterMyEventType doRegisterMyEventType;

//Here is the constructor for MyEvent class which inherits QEvent.
MyEvent::MyEvent()
  : QEvent(myEventType)
{
}

これはどれほど「悪」なのでしょうか?グローバル名前空間の汚染を防ぐために、全体を名前空間でラップすることもできます。

役に立ちましたか?

解決

は静的レベルの初期化は、他の人が言及したように巨大なコンパイラに依存灰色の領域です。しかし、機能レベルの初期化は、灰色の領域ではなく、あなたの利点に使用することができます。

static inline int GetMyEventType()
{
    static int sEventType = QEvent::registerEventType();
    return sEventType;
}

MyEvent::MyEvent()
  : QEvent(GetMyEventType())
{
}

このソリューションは、registerEventTypeが、あなたのイベントタイプを必要とする前に、あなたは良いですが、静的な初期化、中にMyEventを構築する場合でも呼び出されることが保証されるという特性を持っていますが、それはMyEvent可能だ場合には、スレッドの安全性の問題にあなたを開くん複数のスレッド上に構築される。

ここで、ブースト::なcall_onceに基づいて、スレッドセーフなバージョンです。

#include "boost/thread/once.hpp"

static boost::once_flag sHaveRegistered = BOOST_ONCE_INIT; //This is initialized statically, effectively at compile time.    
static int sEventType = -1; //-1 is not a valid event

static void DoRegister()
{
    sEventType = QEvent::registerEventType();
}

static inline int GetMyEventType()
{
    boost::call_once(sHaveRegistered, &DoRegister);
    return sEventType;
}

他のヒント

C++の初期化以降 TU全体で これは実装の余地が大きい大きなグレーゾーンです。私はそれを完全に廃止し、いつ何を行うかを明確にすることを好みます。(この保証の欠如による初期化命令の拒否は、 シングルトンクラス 具体的には、これは、定数式で初期化できないグローバル状態 (グローバル変数、静的データ メンバー、および関数ローカル静的変数) はすべて 1 つの TU で初期化する必要があり、その TU が次の機能を実装することを意味します。 主要.

手動の場合、これは、以下を含む翻訳単位にコードを挿入して更新することを意味します。 主要 そしてで 主要 自体。このようなコードの最も一般的な例は、 srand(time(0)) 種を蒔く std::rand PRNG。

プリプロセッサを使用すると、手動コード管理をリファクタリングできます。

// the implementation file for main, could be named main.cpp

#include "whatever_declares_the_real_main.hpp"

#include "global_objects.inc"

int main(int argc, char* argv[]) try {
#include "main_init.inc"

  return the_real_main(argc, argv);

  // main.cpp has well-defined responsibility:
  // initialize global state before passing control to another function, and
  // handle return-code or exceptions

  // you can modify this, depending on your preference and desired API
  // for example:
  return the_real_main(std::vector<std::string>(argv+1, argv+argc));
  return the_real_main(parse_args(argv+1, argv+argc));
  // just make sure to keep main.cpp's responsibility well-defined and
  // relatively simple
}
// example handling; depending on your specifics, you might do something
// different, or know how to provide more information:
catch (std::exception& e) {
  std::cerr << "abnormal termination: " << e.what() << '\n';
  return 1;
}
catch (...) {
  std::cerr << "abnormal termination.\n";
  return 1;
}

これらの .inc ファイルはヘッダーでも実装ファイルでもありません。ヘッダーや実装ファイルに一般的に使用される .h、.hpp、.cc、.cpp などの拡張子を使用しない限り、正確なファイル拡張子は問題ありません。生成できます グローバルオブジェクト.inc そして main_init.inc ファイル命名規則に基づいており、依存関係を含めることができるようにインクルード ガードを使用します (ヘッダーに対してインクルード ガードが機能するのと同じように)。

たとえば、これらのファイルはどちらも次のように対応します。 myevent.hpp そしてそのヘッダーの横に配置されます。

// file "myevent.global_inc"
#ifndef INCLUDE_GUARD_37E6F5857F8F47918A7C83F29A9DA868
#define INCLUDE_GUARD_37E6F5857F8F47918A7C83F29A9DA868

#include <QEvent.hpp> // or whatever headers you need

#include "myevent.hpp" // declares the variable defined just below
// (remember you use 'extern' to declare objects without defining them)

int your_namespace::myEventType = QEvent::registerEventType();

#endif

// file "myevent.main_inc"
#ifndef INCLUDE_GUARD_4F1B93D0F4D3402B802CBA433241AA81
#define INCLUDE_GUARD_4F1B93D0F4D3402B802CBA433241AA81

// nothing needed in this case, from what you've shown so far

// this is where you place expressions that would otherwise require a dummy
// global variable to make sure they are executed, but this also allows use
// of temporary variables while includes handle dependency order:
#include "something_else.main_inc" // fake example dependency, which must
{                                  // be executed first
  int temp;
  some_func(&temp);
  other_func(temp); // not easy to transform this into a global's init
  // expression, yet defining it this way is natural, because it's exactly
  // how you would do it inside a function
}

#endif

定数式による静的データの初期化のみが必要な場合は、他のすべての手法よりもそれが優先されることに注意してください。その初期化の主な制限は、関数呼び出しを行うことができないことです (しかし、実際にはもっと複雑です)。したがって、それはあなたの場合には当てはまりません。これは、C で実行できる唯一の種類のグローバル変数初期化です。詳細については、こちらをご覧ください。

私は「静的レジスタオブジェクト」パターンにかなりのビットを使用していますが、一つの大きな問題を認識しなければなりません - あなたはに登録されているもので、おそらく静的であるとされ、それ自体は、事前に作成されていることを確認する必要がありますあなたが登録しています。 C ++は、翻訳単位間の静的な構造の順序を保証するものではありませんので、これは問題となる可能性があります。一つの解決策は、いわゆるマイヤーシングルトンを使用することです。

class Registry {
  public:
    static Registry & Instance() {
        static Registry r;
        return r;
    }

    ... 

 private:
    Registry() {    
      ...
    }
};
レジストリへのすべての参照は、インスタンス()メソッドを経由しなければならないとして、

、あなたが必要な建設順序を保証されています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top