序文:
C++ は、プログラマがグローバル変数を定義して使用できるようにする、非常に一般的に使用されるプログラミング言語です。グローバル変数はプログラムで広く使用されますが、アクセスがグローバルであるため、潜在的な問題が発生する可能性があります。この記事では、C++ グローバル変数のインストルメンテーションの拡張機能について説明し、考えられる問題と解決策について説明し、いくつかの実践的な例を示します。
まず、グローバル変数の定義と使用
C++ のグローバル変数は関数の外部で定義できるため、グローバル変数にグローバル スコープが与えられます。この場合、グローバル変数は、それが定義されている関数だけでなく、プログラム全体に表示されます。
グローバル変数は次のように定義されます。
int global_var = 10;
グローバル変数を定義すると、プログラム内のどこからでもアクセスできるようになります。たとえば、関数では次を使用してこのグローバル変数にアクセスできます。
void foo(){
std::cout << global_var << std::endl;
}
この例では、関数 foo() を定義します。この関数では、グローバル変数 global_var にもアクセスできます。
第二に、C++ グローバル変数の問題
グローバル変数は完全に無害というわけではありません。グローバル変数にはグローバル スコープがあるため、どのコードでも変更できます。これにより、次のような潜在的な問題が発生する可能性があります。
1. 名前空間の汚染
グローバル変数が定義されると、変数の名前はプログラム全体の名前空間の一部になります。したがって、プログラム内に複数のグローバル変数が存在する場合、それらの名前が重複して名前空間の汚染につながる可能性があります。これにより、コードの保守が困難になる可能性があります。
名前空間汚染のケースコード:
// 文件1:global_var1.cpp
int global_var = 0;
// 文件2:global_var2.cpp
int global_var = 1;
// main.cpp
#include <iostream>
int main(){
std::cout << global_var << std::endl; // 编译错误:multiple definition of 'global_var'
return 0;
}
このコードでは、2 つの異なるファイル (global_var1.cpp と global_var2.cpp) に同じ名前の 2 つのグローバル変数を定義します。main.cpp でこれらのグローバル変数にアクセスしようとしますが、同じ名前のグローバル変数が複数あるため、リンク エラーが発生します。
2. プログラムのセキュリティ問題
グローバル変数はプログラム内のどこからでもアクセスできるため、セキュリティ上の問題が発生する可能性があります。たとえば、グローバル変数にパスワードなどの機密情報が保存されている場合、プログラム内の誰でもその変数にアクセスして変更できます。
プログラムのセキュリティ問題のケース コード:
#include <iostream>
#include <string>
// 存储用户密码的全局变量
std::string password = "12345678";
// 函数1:读取密码
void read_password(){
std::string input_password;
std::cout << "请输入密码:" << std::endl;
std::cin >> input_password;
if(input_password == password){
std::cout << "密码正确" << std::endl;
} else{
std::cout << "密码错误" << std::endl;
}
}
// 函数2:修改密码
void change_password(){
std::string new_password;
std::cout << "请输入新密码:" << std::endl;
std::cin >> new_password;
password = new_password;
std::cout << "密码修改成功" << std::endl;
}
// main函数
int main(){
read_password();
change_password();
read_password();
return 0;
}
このコードでは、グローバル変数 (パスワード) を使用してユーザーのパスワードを保存します。パスワードの読み取りとパスワードの変更のために、それぞれ 2 つの関数 (read_password と change_password) を定義します。グローバル変数はプログラム内のどこからでもアクセスできるため、このパスワードはどこからでも読み取りおよび変更できるため、セキュリティ上のリスクが生じます。
3. マルチスレッドの問題
グローバル変数もマルチスレッドの問題を引き起こす可能性があります。複数のスレッドがグローバル変数に同時にアクセスして変更すると、競合状態やデッドロックが発生する可能性があります。
マルチスレッドの問題のケースコード:
#include <iostream>
#include <thread>
// 全局变量
int global_var = 0;
// 线程1:不断增加全局变量
void thread1(){
for(int i = 0; i < 100000; i++){
global_var++;
}
}
// 线程2:不断减少全局变量
void thread2(){
for(int i = 0; i < 100000; i++){
global_var--;
}
}
// main函数
int main(){
std::thread t1(thread1);
std::thread t2(thread2);
t1.join();
t2.join();
std::cout << "global_var = " << global_var << std::endl; // 输出:global_var = -4629
return 0;
}
このコードでは、グローバル変数 (global_var) をそれぞれインクリメントおよびデクリメントし続ける 2 つのスレッド (thread1 と thread2) を定義します。2 つのスレッドが同時にグローバル変数にアクセスして変更するため、競合状態やデッドロックが発生する可能性があります。
3. C++ グローバル変数の検出の強化
これらの考えられる問題を解決するために、次の側面を含むいくつかの強化された検出方法を使用できます。
1. 名前空間でグローバル変数を定義する
名前空間でグローバル変数を定義すると、グローバル変数が他の変数と同じ名前を持つことを防ぐことができます。例えば:
namespace my_namespace{
int global_var = 10;
}
このようにして、プログラム全体で、my_namespace::global_var
によってこのグローバル変数にアクセスできます。
2. 不変のグローバル変数の代わりに定数を使用する
グローバル変数の値が変更されない場合は、グローバル変数を定義するときに代わりに定数を使用することをお勧めします。例えば:
const int global_const_var = 10;
この例では、定数を使用してグローバル変数を定義します。これは定数であるため変更できないため、コードの安全性が高まります。
3. グローバル変数の代わりにローカル変数を使用する
場合によっては、グローバル変数の代わりにローカル変数を使用できます。変数が 1 つの関数でのみ使用される場合は、グローバル変数を定義する代わりに、この関数でローカル変数を定義できます。例えば:
void foo(){
int local_var = 10;
std::cout << local_var << std::endl;
}
この例では、グローバル変数を定義する代わりに、ローカル変数 local_var を定義します。
4. カプセル化メカニズムを使用する
グローバル変数をプログラム内の複数の場所で使用する必要がある場合は、カプセル化メカニズムを使用することをお勧めします。カプセル化メカニズムを使用すると、グローバル変数と関連関数をクラス内にカプセル化できます。このアプローチにより、クラスのメンバー関数のみがグローバル変数にアクセスできるようになります。例えば:
class GlobalVar{
public:
static int get_global_var(){
return global_var;
}
private:
static int global_var;
};
int GlobalVar::global_var = 10;
void foo(){
std::cout << GlobalVar::get_global_var() << std::endl;
}
この例では、静的メンバー変数 global_var と静的メンバー関数 get_global_var() を含むクラス GlobalVar を定義します。静的メンバー変数と静的関数を使用すると、クラスのメンバー関数のみがグローバル変数にアクセスできるようになります。
5. シングルトンパターンを使用する
シングルトン パターンは、クラスのインスタンスが 1 つだけ存在することを保証する設計パターンです。グローバル変数をプログラム内の複数の場所で使用する必要があり、インスタンスが 1 つだけ存在することを確認する必要がある場合は、シングルトン パターンを使用できます。例えば:
class GlobalVar{
public:
static GlobalVar* instance(){
static GlobalVar gv;
return &gv;
}
int get_global_var(){
return global_var;
}
private:
GlobalVar(){
global_var = 10;
}
int global_var;
};
void foo(){
std::cout << GlobalVar::instance()->get_global_var() << std::endl;
}
この例では、クラス GlobalVar を定義し、シングルトン パターンを使用して、インスタンスが 1 つだけ存在するようにします。コンストラクターでは、グローバル変数 global_var を初期化します。シングルトン パターンを使用してインスタンスが 1 つだけ存在することを確認し、get_global_var() 関数を使用してクラスのメンバー関数のみがグローバル変数にアクセスできることを確認します。
要約:
C++ のグローバル変数は、潜在的な問題を引き起こす可能性があります。これらの問題を解決するには、名前空間でのグローバル変数の定義、不変グローバル変数の代わりに定数の使用、グローバル変数の代わりにローカル変数の使用、カプセル化メカニズムの使用、シングルトン モードの使用など、いくつかの強化された検出方法を使用できます。上記のスキームは、特定の状況に応じて使用できます。