web-dev-qa-db-ja.com

静的データメンバーの初期化

静的データメンバーの初期化をクラス外にする必要があるのはなぜですか?

class X
{
public:
      int normalValue = 5; //NSDMI
      static int i;
};

int X::i = 0;

静的データメンバー(ここでは "i")が宣言ではなく定義であるのはなぜですか?

20
CyberGuy

初期値が何であるかを示すinitializerdefinitionを区別することが重要です。この変更されたコードは有効であり、クラス定義に初期化子があります。

class X
{
public:
  int normalValue = 5;
  static const int i = 0;       // declaration, with initializer
};

const int X::i;                 // definition

つまり、クラスの外側にある必要があるのは定義であり、初期化ではありません。

これは、変数がメモリ内にアドレスを持つ必要があるためです(コンパイル時の定数式などの限られた状況でのみ使用される場合を除きます)。

非静的メンバー変数は、それがメンバーであるオブジェクト内に存在するため、そのアドレスは、それを含むオブジェクトのアドレスに依存します。新しいXを作成するたびに、新しいX::normalValue変数も作成します。非静的データメンバーの有効期間は、クラスのコンストラクターから始まります。 NSDMI構文は、メモリ内の変数のアドレスとは何の関係もありません。明示的なコンストラクター初期化子リストを使用してすべてのコンストラクターで初期値を繰り返す代わりに、1か所で初期値を提供できます。

一方、静的メンバー変数はクラスのインスタンス内には含まれず、単一のインスタンスとは独立して存在し、プログラムの最初から固定アドレスに存在します。静的メンバー変数(またはその他のグローバルオブジェクト)が一意のアドレスを取得するには、リンカーは静的変数の1つの定義を1つのオブジェクトファイルで確認し、それにアドレスを割り当てる必要があります。

静的変数は1つのオブジェクトファイルに1つだけ定義する必要があるため、クラス定義は通常ヘッダーファイルに存在し、複数のオブジェクトファイルに含まれるため、その定義をクラスで提供することは意味がありません。したがって、クラスで初期化子を提供することはできますが、静的データメンバーをどこかに定義する必要があります。

extern変数を宣言するように見ることもできます:

namespace X {
  extern int i;
}

これは変数を宣言しますが、プログラムのどこかに定義がなければなりません:

int X::i = 0;
36
Jonathan Wakely

静的データメンバー(C++ 11で定義されているようにodr-usedの場合)に別の定義を提供する必要があります。これは、その定義がどこかに存在する-ただ1つの変換単位にあるためです。静的クラスデータメンバーは、基本的にクラススコープで宣言されたグローバルオブジェクト(グローバル変数)です。コンパイラーは、各グローバルオブジェクトの実際の「本体」を保持する特定の変換単位を選択することを求めています。実際のオブジェクトをどの翻訳単位に配置するかを決めるのはあなたです。

7
AnT

「静的」クラスメンバーはグローバルに割り当てられた変数(単一のクラスインスタンスとは関係ありません)のようなものです。グローバル変数。

単純なクラスメンバー(非静的)は、クラスインスタンスに割り当てられたメモリブロックに存在します。

3
Viktor Latypov

単純な理由は、クラスは通常headerファイルで宣言されるためです。これは多くの場合、複数のcppファイルに含まれています。静的データメンバーには外部リンケージがあり、クラス内での定義に適さない1つの変換単位で宣言する必要があります。

Juanchopanzaが指摘するように、次のことが許可されています。

struct A
{
    const static int i = 1;
};

ただし、これはdeclarationにすぎず、定義ではありません。 iのアドレスをどこかで使用する場合は、それを定義する必要があります。例えば:

f(int);
g(int&);

X<A::i> x; // Okay without definition for template arguments
char a[A::i]; // Okay without definition, just using value as constant expression
&A::i; // Need a definition because I'm taking the address
f(A::i); // Okay without definition for pass by value
g(A::i); // Need a definition with pass by reference
2
Jesse Good

静的データメンバーがconst列挙型のconst整数型である場合、宣言時に静的データメンバーを初期化することが可能であることを覚えておいてください。

C++ 03標準から、§9.4.2

静的データメンバーがconst整数型またはconst列挙型である場合、クラス定義での宣言により、定数定数式(5.19)である定数初期化子を指定できます。

struct Foo {
  static const int j = 42; // OK
};
1
juanchopanza

コンパイラーがユニットからバイナリー・コードを生成すると(非常に単純化:cppファイルとそれに含まれるすべてのヘッダー)、静的変数のシンボルが生成され、最終的にその変数の初期化コードが生成されます。

静的変数シンボルが複数のユニットで宣言されることは問題ありませんが、それが複数回初期化されることは問題です。

したがって、初期化コードが1つのユニットに対してのみ発行されることを確認する必要があります。つまり、静的変数は正確に1つのユニットで定義する必要があります。

1
Gigi

静的データメンバー

#include<iostream.h>
#include<conio.h>

class static_var
{

static int count; //static member of class
public :

void incr_staticvar()
{
count++;
}

void outputc()
{ 
cout<<"Value of Static variable Count :- "<<count<<endl;
}
};

int static_var::count;

void main()
{
clrscr();
static_var obj1,obj2,obj3,obj4;

obj1.incr_staticvar();
obj2.incr_staticvar();
obj3.incr_staticvar();
obj4.incr_staticvar();

cout<<"\nAfter Increment of static variable by Four Different objects is :-\n";

obj1.outputc ( );
obj2.outputc ( );
obj3.outputc ( );
obj4.outputc ( );

getch();
}
0