CとC++の間で、関数の外部でstatic
として宣言された変数に違いはありますか。 static
はファイルスコープを意味し、変数はファイルの外部からアクセスできないことを読みました。また、Cでは、グローバル変数はstatic
であることも読みました。つまり、Cのグローバル変数に別のファイルでアクセスできないということですか?
いいえ、この点でCとC++の間に違いはありません。
読む this SO answer Cプログラムでのstatic
の意味について。C++では、クラス変数の代わりにstatic
を使用することに関連する他のいくつかの意味があります。インスタンス変数)。
グローバル変数がstatic
であることに関して-メモリ割り当ての観点からのみ(すべてのグローバルがそうであるように、それらはデータセグメントに割り当てられます)。可視性の観点から:
static int var; // can't be seen from outside files
int var; // can be seen from outside files (no 'static')
ここには、「static linkage(またはscope)」とstatic allocation "の2つの概念があります。
関数の外側では、キーワードはリンケージを指し、関数の内側では割り当てを指します。関数外のすべての変数には、暗黙的に静的割り当てがあります。不幸なデザインかもしれませんが、あります。
CとC++は同じです。
staticは2つの異なることを行います。
関数スコープ外で宣言された変数の場合、変数の可視性(リンケージ)が変更されます。変数は関数スコープ外にあるため、グローバル変数になります。静的でない場合は、ユニバーサルリンケージ(可視性)があるため、これとリンクされているすべてのコードがアクセスできます(外部として宣言する必要がある場合があります)。変数が関数スコープの外にあり、静的である場合でも、常に存在し、その値を保持するという点でグローバル変数ですが、同じコンパイルユニット(.cファイルと.hが含まれている)の外部のコードはアクセスできません。
関数スコープ内で宣言された変数の場合、staticは変数が格納される場所を変更します。静的でない場合は自動変数になります。つまり、関数が終了すると消え、関数が再び入力されると(スタック上で)再び存在します。これは、関数を終了するとその値を失います。また、関数の終了後は、それへの参照(それへのポインター)は無効になります。関数スコープ内で宣言された変数が静的である場合、それは自動変数ではなく、グローバルに割り当てられた変数になります。したがって、変数は関数の終了後に存在するため、関数の呼び出し間でその値を保持し、関数の終了後も変数への参照(ポインター)は有効です。どちらの場合も、変数のスコープはその関数内にのみあるため、関数スコープの外部から直接(ただし保存された参照を介してのみ)アクセスすることはできないことに注意してください。
Staticが最後に行うことの1つは、変数の初期化子(つまり、int foo = 5)が実行されたときの変更です。割り当てがグローバルであるすべての場合(自動割り当てを除くすべての場合)、初期化子はプログラムの実行の開始時に1回だけ実行されます。 main()が実行される前に実行されるため、初期化子が定数だけでなくコードを実行すると、予期しない結果が生じる可能性があります。自動の場合、初期化子は関数が入力されるたびに実行されます。この場合、初期化子は常にmain()が入力された後に実行されます。
サザンホスピタリティの答えに追加したい CおよびC++の静的変数
次の備考:
「ローカルから変換ユニット」を示すためのstatic
の使用は、C++では非推奨です(href = "https://rads.stackoverflow.com/amzn/click/com/0201700735" rel = "nofollow noreferrer" C++プログラミング言語:特別版、付録B.2.3、非推奨の機能)。
代わりに、名前のない名前空間を使用する必要があります。
_static int reply = 42; // deprecated
namespace {
int reply1 = 42; // The C++ way
}
_
サザンホスピタリティがすでに述べたように、グローバルオブジェクトの初期化の順序は定義されていません。そのような状況では、href = "http://en.wikipedia.org/wiki/Singleton_pattern#C.2B.2B"、シングルトンパターンの使用を検討する必要があります。
更新:GManは私の答えにコメントしました:
「順序は翻訳単位ごとに定義されています...」:これは本当に頭がおかしくなったので、C++プログラミング言語で調べました。
セクション9.4.1、非ローカル変数の初期化で、Stroustrup教授は、「参照を返す関数はグローバル変数の優れた代替手段である」と示唆しています。
_int& use_count()
{
static int uc = 0;
return uc;
}
_
「use_count()
の呼び出しは、最初の使用時に初期化されるグローバル変数として機能するようになりました。例:」
_void f()
{
std::cout << ++use_count() << '\n';
}
_
私の理解では、これはシングルトンパターンと非常によく似ています。
GManはさらに次のようにコメントしています。「これらのオブジェクトを作成する機能を1つに制限し、グローバルアクセスを提供する必要があります。」 1つに制限することは、本当に問題の何かに関係していますか?世界的に必要かもしれませんが、他の場所では必要ないと誰が言いますか?」
Singleton(127)からの引用(Gamma et al、Design Patterns):
「シングルトンパターンは、グローバル変数を改善したものです。これにより、唯一のインスタンスを格納するグローバル変数で名前空間が汚染されるのを防ぎます。」
「このパターンにより、考えを簡単に変えて、シングルトンクラスの複数のインスタンスを許可できます。」
シングルトンは、最初に使用された順序で初期化されます。
Herb Sutter、Andrei Alexandrescu、C++ Coding Standardsの項目10には、次のように書かれています。
「共有データ、特にグローバルデータは避けてください。」
したがって、グローバルデータを避けるためにシングルトンをよく使用します。しかしもちろん、すべてが使いすぎであるため、これはシングルトンパターンの使いすぎである可能性があります。 (Johshua Kerievskyは、彼の著書「Refactoring toPatterns」でこれを「Singletonitis」と呼んでいます。)
更新2:
(申し訳ありませんが、コメントを書くことができないため、このアップデートです。)
ジャルフは彼のコメントに次のように書いています。「4人組がシングルトンパターンについて書いたとき、違法なものを吸っていました。」
明らかに、他のC++開発者も興味深い物質を吸っていました。たとえば、Herb Sutter(彼は、2番目のC++標準であるC++ 0xの開発中にISOC++標準委員会の秘書兼委員長を10年以上務め、MicrosoftでC++/CLIのリードアーキテクトを務めました。Herbは現在Microsoftプラットフォーム用のPrismメモリモデルと並列プログラミング用のVisualC++のConcur拡張機能の設計者)は、C++コーディング標準の項目21に次のように書いています。
「別の変数に依存する可能性のある(名前空間レベルの)変数が必要な場合は、シングルトンデザインパターンを検討してください。慎重に使用すると、オブジェクトが最初のアクセス時に初期化されるようにすることで、暗黙的な依存関係を回避できます。それでも、シングルトンはのグローバル変数です。羊の服であり、相互依存または周期的な依存関係によって壊れています。」
したがって、可能であれば、グローバルデータは避けてください。ただし、グローバルデータを個別の変換単位で使用する必要がある場合は、特定の初期化シーケンスを適用するための許容可能なソリューションはシングルトンです。
Java言語では、グローバルデータも存在しないことに注意してください。明らかに、グローバルデータはシングルトンデザインパターンを使用して置換/エミュレートされます。
(私はJavaチームでデイジョブを行っているので、C++プログラムとJavaプログラムとの最大の類似性を目指しています。たとえば、すべてのクラスは独自のソースファイル/翻訳ユニットにあります。)
あなたの質問に対する直接の答えではありませんが、CとC++の両方を使用する場合は注意が必要です。
C++では、Cとは異なり、「const」と宣言されたグローバル変数は、「static」が使用されていた場合と同様に、変換ユニットに対して暗黙的にローカルです。
例:
// file A
extern const int glob;
// file B
const int glob = 42;
これは、Cコンパイラを使用しているが、C++コンパイラを使用していない場合に機能します。 C++では、ファイルBのglob変数をファイルAから使用することはできず、リンカーは「未解決の参照」エラーを生成します。