第8講 関数の学習
第5話 関数ポインタによる関数の操作
ここでは関数ポインタをによる関数の操作を考えます。
関数ポインタは、
変数の型 (*ポインタ変数名)(引数の型)=関数名;
で宣言され、関数のアドレスが代入されます。
まず、新規作成から次のようなForm1を作り、
pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
String^ (*p)(void)=f;
label1->Text=p();
}
static String^ f(void){
return L"Visual C++の世界にようこそ!";
}
};
}
とコーティングして下さい。そして、ビルドして実行ボタンを押すと、
となります。
コーティングを次のように変更しても同じ結果が得られます。
pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
label1->Text=f();
}
static String^ f(void){
return L"Visual C++の世界にようこそ!";
}
};
}
つまり、String^ (*a)()=f; でfのアドレスをポインタ変数に代入すると、
p()はf()と同じになります。
しかし、これだと関数fを参照しているのか、関数なのか区別がつきません。
関数の参照であることを明確にするために
p()を(*p)()としても同じ結果が得られます。
pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
String^ (*p)(void)=f;
label1->Text=(*p)();
}
static String^ f(void){
return L"Visual C++の世界にようこそ!";
}
};
}
尚、参照できる関数はstatic関数といわれるものしかできないようです。
staticとは静的という意味です。
それに対して、staticがつかない関数は動的関数といいます。
普通の関数は、関数が終了するとメモリーから消滅します。
そして、呼び出されるとまた発生します。
つまり、普通の関数は生成消滅を繰り返すことによって、
メモリーを節約しています。
それに対してstaticすなわち静的関数は、メモリーの中に常駐しています。
つまり、静的関数を用意しただけメモリーは圧迫されていきます。
今関数で説明したことは、変数でも同様です。
ローカル変数は、関数の消滅とともに消滅します。
それに対して、静的変数は消滅しません。
どうして、普通の関数が参照できないかの理由は、普通の関数は生成消滅を繰り返すので、
使用メモリーが動的に動いてしまうので、
メモリーを固定して使うポインタに向いていないということだと思います。
実は、さらにstatic関数には大きな制約があります。
それは、static関数上からLableやTextBoxやDataGridView等への書き込みができないということです。
これらへ書き込みは普通の関数=動的関数上から行います。今回は関数 System::Void button1_Click上で書き込みをしました。
関数をポインタから参照して使うことにどんな意義があるのでしょうか。
利点の一つは、関数を配列として扱えるという点です。
次のようコーティングを変更して下さい。
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
String^ (*a[3])(void)={f,g,h};
int i;
String^ w=L"";
for(i=0;i<3;i++)w+=(*a[i])()+L"\n";
label1->Text=w;
}
static String^ f(void){
return L"Visual C++の世界にようこそ!";
}
static String^ g(void){
return L"この講座は世界で一番わかりやすいVC++講座です。";
}
static String^ h(void){
return L"VC++のおもしろさを堪能して下さい。";
}
};
}
実行結果
関数の配列を使って、
例えば次のような計算をさせてみましょう。
(1+2+3+4+5+6+7+8+9+10)+(2×3×4×5)+(2+4+6+8+10)
それぞれの()内の計算を関数でやらせ、その関数をポインタによって参照して使います。
コーティングは例えば、次のようにすればすればよいわけです。
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
int (*p[3])(void)={f,g,h};
int i,w;
w=0;
for(i=0;i<3;i++)w+=(p[i])();
label1->Text=w.ToString();
}
static int f(void){
int i,w=0;
for(i=1;i<11;i++)w+=i;
return w;
}
static int g(void){
int i,w=1;
for(i=2;i<6;i++)w*=i;
return w;
}
static int h(void){
int i,w=0;
for(i=2;i<11;i+=2)w+=i;
return w;
}
};
}
また、次のようにコーティングを変更して下さい。
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
int (*a)(int)=f;
label1->Text=(a(3)).ToString();
}
static int f(int w){ //渡された値を2倍にして戻す関数
w=w*2;
return w;
}
};
}
実行結果
これは
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
label1->Text=(f(3)).ToString();
}
static int f(int w){ //渡された値を2倍にして戻す関数
w=w*2;
return w;
}
};
}
としたときと同じです。
これから、f(3)とa(3)が同じであることがわかります。
引数についても関数ポインタは、関数同様に使えるのです。
次の計算も考えてみましょう。
(2×3×4×5×6)÷(1+2+3)÷6÷5
これは、次のようなコーティングが考えられます。
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
double (*p[3])(double)={f,g,h};
int i;
double w;
w=1;
for(i=0;i<3;i++)w=(p[i])(w);
label1->Text=w.ToString();
}
static double f(double w){
double i;
for(i=2;i<7;i++)w*=i;
return w;
}
static double g(double w){
double i,v;
v=0;
for(i=1;i<4;i++)v+=i;
w=w/v;
return w;
}
static double h(double w){
double i;
for(i=6;i>4;i--)w/=i;
return w;
}
};
}
第4話へ 第6話へ