C++BuilderXEでADOConnection使ってSQL Serverに接続してみようとコンポーネントをフォーム上に設置しようとしたら
とか怒られたので、探してみたら何故かC:\の直下に落ちてた。
C++Builderのbinにコピーして事なきを得た。
C++BuilderXEでADOConnection使ってSQL Serverに接続してみようとコンポーネントをフォーム上に設置しようとしたら
とか怒られたので、探してみたら何故かC:\の直下に落ちてた。
C++Builderのbinにコピーして事なきを得た。
2.Additionalコンポーネント
StringGrid1->Rows[0]->Add("0_0"); StringGrid1->Rows[0]->Add("0_1"); StringGrid1->Rows[0]->Add("0_2"); StringGrid1->Rows[0]->Add("0_3"); StringGrid1->Rows[0]->Add("0_4"); StringGrid1->Rows[3]->Add("3_0"); StringGrid1->Rows[3]->Add("3_1"); StringGrid1->Rows[3]->Add("3_2"); StringGrid1->Rows[3]->Add("3_3"); StringGrid1->Rows[3]->Add("3_4");
StringGridのRows[y],Cols[x]プロパティは1行ごとにTStringsの文字列配列になっています。これにRows[y]->Add(“xxx”);を実行して文字を追加できますが、StringGrid->RowCountやColCount以上の文字列を格納しようとしても出来ません。StringGrid->ColCount,RowCountは表示領域だけで無く、RowsやColsプロパティの格納出来る文字列の最大値でもあります。
StringGrid->Cells[x][y]のx,yにColCount,RowCount以上の数値を指定した場合、空文字が取得されます
1.Standardコンポーネント
void __fastcall TForm1::Edit1Change(TObject *Sender) { if(Edit1->Text=="100") ShowMessage("100と入力されました"); if(Edit1->Text=="exit") Close(); }
このサンプルはEdit1のOnChangeイベントで100と入力されたらメッセージボックスで「100と入力されました」とメッセージを表示し、かつ、exitと入力するとアプリケーションを終了するプログラムになっています。
イベントは1度の変更につき1度しか発生しません。同じ文字列の状態でイベントが連続して発生することはありません。
TEditはReadOnlyプロパティをTrueにする事で入力できない状態に設定することも出来ます。
void __fastcall TForm1::Label1Click(TObject *Sender) { ShellExecuteA(Form1->Handle,"open","http://blog.dlo-olb.com",NULL,NULL,0); } void __fastcall TForm1::Label1MouseEnter(TObject *Sender) { Label1->Font->Color=clRed; } void __fastcall TForm1::Label1MouseLeave(TObject *Sender) { Label1->Font->Color=clBlack; }
このサンプルはTLabelのOnClick、OnMouseEnter、OnMouseLeaveイベントを作成しそこにコードを入力します。
MouseEnterはマウスカーソルがTLabelコンポーネントに重なったときに1度だけ発生するイベントで、MouseLeaveはマウスカーソルがTLabelの上から離れたときに1度だけ発生します。
ここではマウスが重なったときTLabelのフォントの色を赤色に変更し、マウスが離れると黒色になり、ラベルをクリックするとこのブログのトップページを開くサンプルです。
ShellExecuteA関数はアプリケーションを関連づけで実行するWindowsが用意しているAPIの一つで、このAPIについて簡単に説明すると、
最初の引数にForm1のHandleというプロパティを渡し
2番目の引数は”open”という文字列を指定
3番目の引数で開きたい関連づけされた拡張子のファイルを指定
4番目の引数は3番目に実行ファイルを指定した場合のパラメータを設定する文字列を渡し必要なければNULLを指定
5番目の引数は作業フォルダの指定で、作業フォルダにしたいフォルダ名の文字列を入力します。必要なければNULLを指定
6番目の引数はアプリケーションをどのような状態で表示するかを設定する番号なのですが、こちらはShowWindow関数というAPIを参考にすると、その引数が書かれているので参考にしてください。通常は0でかまいません。
void __fastcall TForm1::Button1Click(TObject *Sender) { if(CheckBox1->Checked) { ShowMessage("チェックされています"); } }
このサンプルはフォームにTButtonとTCheckBoxを一つずつ設置し、TButtonのクリックイベントでCheckBox1のCheckedプロパティがTrueの場合、メッセージを表示します。(if文は比較式に入れた値が1(True)の場合は条件が成立しているという意味になります。)
void __fastcall TForm1::Button1Click(TObject *Sender) { ListBox1->Items->Add(Edit1->Text); }
ボタンをクリックするたびにEdit1に入力した文字列をListBox1に格納して表示します。
9.名前空間
void __fastcall TForm1::Button1Click(TObject *Sender) { }
これはTButtonのOnClickイベントを作成した状態のCPPファイルに書かれる内容です。
ここで今注目してほしいのは、TForm1::という表記ですが、この::というのはTForm1の中に定義されていますと示す物で、この::の事をスコープ演算子といい、この時のクラス名の事を名前空間と呼びます。
関数名を記述するとき、クラスの外部にクラスのメンバー関数の内容を書くときは、必ずこの名前空間を示す::を用いた記述をしなければなりません。
なぜクラスを使うのに->や.を使わないのかというと、->や.はクラスをメモリ上に展開したときに利用する物で、この名前空間はコンパイラが変数や関数の位置を認識するための物であり、そもそもの用途が違うという理由があります。
クラス名=名前空間と説明しましたが、名前空間はクラス名だけでなく、namespace 名前{};というコードブロックを用いることで、クラスとは関係の無い変数や関数にも名前空間を与えることが出来るようになっています。
クラス名は通常の型名と同じように変数の型として利用できますが、この通常のnamespaceを使った場合の名前は型名には出来ません。
namespace XXX{ int x; int func(){ return 10; } }; namespace ZZZ{ int x; }; XXX::x=100; ZZZ::z=500; Caption = XXX::x + XXX::func();
このサンプルはnamespaceコードブロックを利用した名前空間のサンプルです。クラスや構造体とは違い、通常の変数や関数、場合によってはクラスを分類で分けるために利用されます。
namespace aaa{ int x; }; namespace bbb{ namespace ccc{ int c; }; int x; }; namespace aaa{ int y }; aaa::x=40; aaa::y=30; bbb::x=50; bbb::ccc::c=100;
このようにnamespaceの名前が重複した状態でも問題はありませんが、重複した名前空間の中に同じ名前の変数を書くことは出来ません。重複した名前空間は一つの名前空間と認識されます。
名前空間bbbの中に名前空間cccが宣言されていますが、このように名前空間の中に名前空間を収めることも出来るようになっています。
namespace XXX{ int aa; int xx; }; using namespace XXX; int xx; aa=100; XXX::xx=50; ::xx=33;
using namespace XXX;によってXXX::の記述を省略しているのがaa=100;の表記です。
しかし、XXXの中にはxxという変数が宣言され、グローバル変数にも同じxxという変数が宣言されています。これではxx=100;としただけではどちらのxxの事かコンパイラが理解できません、この場合はxxについては名前空間を省略して書くことは出来ません。XXXの中の物はXXX::xx=50;、グローバル変数の場合は ::xx=33;という記述になります。
class _base{ protected: int p_x; private: int m_x; public: int a; int ret_x(){ return m_x; } _base(){ m_x=999; a=5; } }; _base *b=new _base(); Caption=b->ret_x(); delete b;
ここで注目するところは、protected: private: public:という部分です。
クラスでは、このprivate:という表記の下に変数や関数を書くと、そのクラスの中だけでしか利用できない変数や関数を定義することが出来るようになります。
public:の下に書くと、その変数や関数は外部からもアクセス出来るようになります。
これにどういうメリットがあるかというと、外部から直接アクセスしてクラス内で利用する重要な変数に全く意味の異なる値が代入されたりすることが防げると言うことです。
クラス内で独自に計算して設定している値に外部からアクセス出来てしまうというのは言い換えると、自分の家の金庫の位置を他の人が直接移動させるのと同じような意味です。
自分で管理しているのに、勝手を知ってるからと、他人や友人に部屋のレイアウトを変えられると困りますね。そんな感じです。
public:では自由に使ってもいい変数や関数を置くことで、例えばprivate:に変数を置いてあるとして、public:にその変数の値を貸し出す関数を置いてやると、その関数からクラスの変数の値を借りてくることも出来るようになります。
逆にpublicの関数を実行することで、引数に指定した変数をprivateの変数に代入することも出来るようになります。
次にprotectedですが、ここに書かれた変数は、クラスを単独で使う場合はprivateとして機能するのですが、次で説明する継承によってその動作に違いが出てきます。
class _abc : public _base { protected: private: public: int a_func(){ return p_x; } };
クラスの宣言部にclass _abcの後に : public _baseとあります。
これは、一番上の例で作成した_baseクラスを元に_abcというクラスを作成しています。
この_abcクラスでは関数しか作成していません。ここで注目する所は、_baseのprotectedで宣言したp_xにアクセス出来るということです。
_baseのprivateに宣言された変数にはアクセス出来ませんが、protectedとpublicに宣言された物にはアクセス出来ます。
しかし、_abcクラスを通常使用するときはpublicに宣言された物にしかアクセス出来ません。
クラス外 | クラス内 | 継承先 | |
protected | × | ○ | ○ |
private | × | ○ | × |
public | ○ | ○ | ○ |
このようになります。
構造体でもstruct _base : _abc{}という宣言で継承することが出来ますが、クラスのように変数や関数へのアクセス制限を掛けることは出来ません。
C++Builderで利用されるVCLコンポーネントはクラスで作成されています。エディタの下のタブにUnit1.hという部分があるのでクリックしてみてください。TForm1のクラスの宣言が書かれています。
C++BuilderではVCLを利用するための独自拡張がされているので、__published:という部分がありますが、ここは統合環境が自動的に記入する部分で、通常この下に書き込むことはありません。
TForm1のprivateやpublicに変数や関数を描き込むことで、TForm1のメンバーとして利用することが出来るようになります。
class _base{ protected: int p_x; private : int x; public : int ret_x(){ return x; } _base(){ x=2; } }; class _abc : private _base{ private : int z; public: int a; _abc(){ z=2; } int func(int p){ return p*ret_x(); } }; class _xyz:public _abc{ private: public: _xyz(){ this->a=100; } int xyzfunc(){return a}; }; _abc aa; _xyz xz;
この例では、_abc aa;としてaaを利用するようになっています。このaaからアクセス出来るのは、_abcの変数aと関数funcだけです。_abcクラスの中では、_baseの変数p_xと関数ret_x のみにアクセス出来ます。ret_xは_baseの変数xの値を取ってくるので、結果的に、aaは_baseのxの値を間接的に利用することが出来ます。
宣言をpublicにしてあると、基底クラスのprotectedの有効範囲は派生の派生の派生でもprivate変数として利用することが出来ます。
xzでは、_baseのp_xにアクセスすることは出来ませんし、その_xyzクラスの中からもp_xにアクセスすることは出来ません。
void __fastcall TForm1::Button1Click(TObject *Sender) { class _base{ private: public: _base(){ Form1->Caption="コンストラクタ"; } ~_base(){ Sleep(5000); Form1->Caption="デストラクタ"; } }; _base *b=new _base(); delete b; }
このサンプルで_base *b=(_base*)malloc(sizeof(_base));free(b);とした場合と、_base *b=new _base();delete b;とした場合を確認してみてください。newした方は、5秒後にデストラクタと表示されますが、malloc freeの方はコンストラクタもデストラクタも表示されません。
C++BuilderのVCLは必ずnewで動的に確保しなければいけない仕様になっていて、TButton btn;と、しただけでは
利用できないようになっています。
class _base{ public: int x; _base(){ x=0; } _base(int &a){ x=a; } }; _base *a=new _base(55); Caption=a->x; delete a;
このようにコンストラクタの括弧の中に引数を付けて宣言します。同名の関数が宣言されていますが、これは関数のオーバーロードといい、関数名は同じでも、引数が違えば同名の関数でもOkというC++の機能です。
ここで、newでメモリを確保するときクラス名の後ろに括弧を付けて、その中にコンストラクタに割り当てた引数と同じ変数などを指定することでコピーコンストラクタの_base(int &a)が実行されメンバのxがその値で初期化されます。
class _base{ public: int x; virtual void setup(){ x=0; } }; class _abc:public _base{ public: void setup(){ x=100; } }; class _xyz:public _base{ public: void setup(){ x=999; } };
このように、基底クラスで宣言されたsetup関数を派生先で、再度、同名、同引数の関数を宣言することで派生元の関数を上書きします。この関数を上書きすることをオーバーライドと呼びます。この手法のメリットは、_abcや_xyzを_baseクラスにキャストする事により、同一のポインタ変数からそれぞれの派生先で上書きされた関数が実行できるということです。
void baseSet(_base *b) { b->setup(); } void __fastcall TForm1::Button1Click(TObject *Sender) { _abc *a=new _abc; _xyz *z=new _xyz; baseSet((_base*)a); baseSet((_base*)z); Caption=z->x; delete a; delete z; }
このように外部の関数で基底クラスを引数に取ることで、派生クラスをキャストさせた状態で参照させることにより、基底クラスで宣言したsetup()を実行すると、派生先の関数で上書きされたsetup()が実行されるようになります。
void __fastcall TForm1::Button1Click(TObject *Sender) { TButton *btn=dynamic_cast<TButton*>Sender; ShowMessage(btn->Name+"が押されました"); }
Button1のOnClickイベントに上のサンプルのように書き、このほかにButton2を用意します。そしてButton2のイベントでOnClickの所のプルダウンメニューを開き、Button1Clickを選択します。
これを実行すると、クリックしたボタンの名前を表示するメッセージが表示されるようになります。
TButton *btn=dynamic_cast<TButton*>Sender;の命令で、Senderに格納されたアドレスをTButtonのポインタに変更し、btnに入ったアドレスがクリックされたボタンコンポーネントなので、その名前を表示できるようになります。
ここで注意することは、dynamic_castというキャストの方法で、このdynamic_castはSenderに格納されている値が必ずTButtonか、それより前の基底クラスでなければ例外を送出します。
仮に、Button1ClickをTPanelのOnClickなどに設定してPanelをクリックするとSenderに格納されるアドレスは、TButtonではないので例外が発生します。発生した例外を受けて対応する処理を書くには、次のように修正します。
TButton *btn; try{ btn=dynamic_cast<TButton*>(Sender); ShowMessage(btn->Name+"が押されました"); }catch(...){ ShowMessage("Button1Clickで例外が発生しました"); }
例外は意図的に発生させるエラーで通常のエラーとの違いは例外が発生するであろう場所にtry catch文を配置することで、例外を補足し、適切な処理を実行することでエラーを回避できることです。
btn=(TButton*)Sender;でも今回のプログラムでは実行しただけでは不具合はありませんが、TPanelなどを割り当ててもエラーが起こらず実行できてしまいます。これでは万が一TButtonにしかない機能を使った場合、回避できないエラーとなってしまい危険です。
今回(TButton*)でキャストする方法でエラーにならない理由は、NameプロパティがTButton、TPanelどちらもが継承しているTComponentから継承された物だからです。
これまで、変数の宣言は以下のようにしてきました。
char *name; int hp; int mp; int attack; int defense;
これまでの知識では、たとえばゲームのキャラクターを作るときは、このようにキャラクターごとに、この大量の変数を個別に宣言しなければなりませんでした。それではダラダラと長くなってキャラクターごとにどの変数を割り当てていたか間違ってしまうこともあると思います。このような場合に、これらをひとまとめにしたい時、構造体という物を利用します。
構造体の記入方法は以下の通りです。
struct CHARACTER{ char *name; int hp; int mp; int attack; int defense; };
これで、CHARACTERというキャラクターの代表的なパラメータを一度に宣言できる変数の型のようなものが作成された事になります。
これを利用するときは
CHARACTER maou,yuusha;
と、通常の変数を宣言するときのように利用します。この構造体の中にある変数へアクセスする時は次のようにします。
CHARACTER maou; char c[]="Maou"; maou.name=c; maou.hp=100; maou.mp=100; maou.attack=50; maou.defense=20;
このように、変数名の後ろに.(ドット)演算子を付けることで構造体の中の個別の変数にアクセス出来ます。このときの.は小数は全く関係ないので、アクセスする時のルールとして覚えてください。もちろん数字の後ろに.を付けて数字を書けばそれは小数ですが、変数名の後ろに.を付けたらそれは構造体へのアクセスという意味になります。もちろん構造体以外の変数では使えません。
struct _abc{ int a; int b; int c; }; _abc *x=new _abc; x->a=100; x->b=200; x->c=300; Caption = x -> a + x -> b + x -> c ; delete x;
このようにアクセスします。.(ドット)の代わり->(矢印演算子)が使われるようになります。
構造体のメモリを確保した場合、deleteはその構造体のポインタを記述するだけでOKです。わざわざ構造体のメンバーすべてに対してdeleteする必要はありません。
ポインタなので次のような書き方も出来ます。
(*x).a=100;
実体からアクセスするときは.を使い、アドレスからメンバーを参照するときは->を使うようになります。
アドレスからアクセスする時は->を使うと言うことは、ポインタを使わない通常の変数宣言の場合、次のように書くことも出来ます。
(&x)->a=100;
struct _abc{ int a=0; int b=0; };
初期化をしたいときは次のようにします。
struct _abc{ int a; int b; _abc(){ a=100; b=0; } };
これはコンストラクタと言って変数が宣言されたと同時に実行される関数です。
ちなみに、変数が解放されるときに実行されるものをデストラクタと言い、次のように書きます。
struct _abc{ int a; int b; ~_abc(){ Form1->Caption="デストラクタ"; } };
デストラクタはコンストラクタと同じく構造体の名前を書きますが、頭に~(チルダ)を付けます。
通常デストラクタではコンストラクタの初期化で確保したメモリを解放する処理を書いたりします。
struct _abc{ int a; int b; int func(){ return a+b; } _abc(){ a=10; b=5; } }; _abc x; Caption = x.func();
ここまで説明しましたが、実際の所、構造体で関数を駆使して使用するというシーンはほとんどありません。それは何故かというと、クラスが構造体の上位版とも言える機能を持っているからで、C++の機能とは言え、クラスの概念はほぼすべての言語で利用されています。構造体でもここまでは出来る程度に覚えておいてください。
int size=10; char *c=new char[size]; ここに*cを利用したプログラムを書く delete c; char *d=(char*)malloc(size); ここに*dを利用したプログラムを書く free(d);
これがnewとmallocの使い方です。やっていることは全く同じ意味です。char c[10];と違うところは、サイズの指定に変数が使えると言うことです。char c[size];とは宣言できません。
動的に確保したメモリは、プログラマーが責任を持って解放しなければなりません。解放しないと使っていないのに使われたままになる、いわゆるメモリーリークという状態になってしまいます。静的確保では関数などのコードブロックが終了したり、プログラムを終了させた時に自動的に解放されますが、動的確保では、delete と free を用いて解放しなければなりません。
delete は new で確保したメモリを解放するための演算子で、freeはmallocなどで確保したメモリを解放する関数です。
char a[]="abcde"; char b[]="12345"; int size=StrLen(a)+StrLen(b)+1; char *c=new char[size]; StrCopy(c,a); StrCat(c,b); Caption=c; delete []c;
上の例では、StrLen関数を使い文字列a,bの長さを取得し、そのサイズでメモリを確保しています。+1してあるのはStrLen関数はNULL文字を含まない文字列の長さを返すので、NULL文字用のサイズを+1してあります。
このように、文字列の長さに合わせた適切な長さの配列を確保してやることが出来るようになります。
動的に確保した配列は、delete [] c;という形でメモリを解放します。配列では無い場合はdelete c;という書き方になります。
プログラムでは同じ処理を何度か実行したいという場合があります。C言語には繰り返し文という物がいくつかあります。そのうちのfor文を説明します。
int x=0; for(int i=1;i<10;i++) { x=x+i; } Caption = x;
このサンプルは1~9を足した物をxに代入しています。
まず文法の説明をします。
for()この括弧の中は 初期値 比較式 制御用の式 が入ります。
for( 初期値 ; 比較式 ; 制御用の式 )
それぞれは;セミコロンで区切ります。
比較式で利用する初期値をまず設定します。初期値は0以外でも数字なら何でもかまいません。そして比較式を書きます。この比較式の条件が成立している間ループすることになります。そして、条件を抜けるために初期値の変数の値を上下させる制御式を書きます。
比較式が成立している間{ }の中(コードブロック)の処理が繰り返されることになります。
void __fastcall TForm1::FormPaint(TObject *Sender) { int x[9][9]; for(int i=0;i<9;i++) { for(int j=0;j<9;j++) { x[i][j]=(i+1)*(j+1); Canvas->TextOut(i*16,j*16,x[i][j]); } } }
このように入れ子にする事も可能です。このサンプルは掛け算の九九の表を作成したことになります。次ような表示になります。
後ほどコンポーネント編で説明しますが、Canvasというフォームのメンバーで、文字や画像を描き込む為の機能があり、それを利用しています。
ここで注意することはforの中の、変数の初期化で変数を宣言している事で、このサンプルのjはjが宣言されたforのコードブロック内でしか利用することが出来ません。iはjが宣言されたforの中でも利用できます。このiやjの事をローカル変数と呼びます。コードブロック内で宣言したローカル変数は宣言したコードブロックの外側からは呼び出せません。すべての関数の外側に宣言した変数のことをグローバル変数といい、この変数はすべての関数から呼び出すことが可能です。
ちなみにswitch文で利用した break;命令を利用することで、forのコードブロックを1段処理を中断して抜けることが出来ます。
for(i=0 ; i<10;i++){
if( i==5 )break;
}
iが5になったらforのコードブロックを抜けます。if文に{ }がありませんが、これはifやforの後に続く命令が1命令だけであれば{ }を省略できるという仕様があるので問題はありません。
int x=0; while(x<10) { x++; }
while文はまず括弧に書かれた比較式を見て条件が成立している間(1の間)のみ、コードブロックの処理を継続します。forと違うところは、初期化はwhileよりも前に書かなければいけないことと、比較式の数値を変更するのはコードブロック内で行うことです。
whileの特徴は最初に比較式を確認しているということです。
int x=10; while(x<10) { Caption="この比較式で、ここは実行されません"; x++; }
このwhile文はxが10で比較式は10<10でfalseなので条件が成立していないのでコードブロック内の処理は一切行われません。
int x=10; do{ x++; Caption=x; }while(x<10);
まずxが10で初期化されています。そしてdo whileのコードブロックが実行され1が加算され、タイトルバーに現在の値を表示します。その後、x<10で比較して成立していないので終了します。
int x=0; if(x==0) { x=1; }else{ x=0; }
if文は if(条件){成立したときの処理}else{成立しなかったときの処理} という文法で入力します。条件は、比較式を入力します。比較式で利用する記号を比較演算子と呼びます。
== 左右の値が同じ
!= 左右の値が違う
< 左の値が右の値より小さい
> 左の値が右の値より大きい
<= 左の値が右の値より小さいか同じ
>= 左の値が右の値より大きいか同じ
これらの比較演算子を使って条件を組み立てます。
上のサンプルの例では、xが0かどうかを調べています。サンプルではxは常に0なので、成立しなかった場合の処理(else)は実行されません。xが0の場合の処理だけを書きたい場合は、else以降は記入しなくても問題ありません。
int x; x=2<10; Caption = x;
この例では2<10の比較結果をxに代入してそれをCaptionに表示しています、ここの例では、2は10より小さいので、結果はtrueなのですが、Stringクラスにbool型を直接読み取る機能が無いため、int型に代入しています。bool型は0か1のどちらかの値なので、整数型であるためint型に格納出来ます。
int x=0,y=0; if(x==0) { if(y==0) { x=1; } }
この表記も許されます。
{ }の中に{ }を何重にも組み合わせることをネスト(入れ子)といい、この階層があまり深くなるようなプログラムは読みにくくなるので書くべきではありません。
しかし、何重にもしなければ比較式が足りないという事もあるかもしれません。こういったときは次のように書くことも出来ます。
int x=0,y=0; if(x==0 && y==0) { x=1; }
条件が2つ書かれて、間に&&という表記が追加されました。これは、左右の条件式が成立している場合のみ、成立しているとみなし、どちらか片方でも成立していなければ成立していないと判断する論理演算子です。
これと同じような役割をするのが、|| と ! です。||は条件のどれか一つでも成立していれば、成立していると見なす演算子です。
int x=0,y=0,z=1; if(x==0 || y==0 || z==0) { x=1; } if(!(x==0)) { こちらは実行されない }else{ こちらが実行される }
この上の例では、zの値は成立していませんが、||で論理演算しているので、他のxかyのどちらかが成立していれば成立しているとされます。
下の例ではx==0は成立していますが、(x==0)の前に!が付いているので、結果が反転して0になります。( )が必要になる理由は、!x==0とすると、演算子の優先順位により、xが反転した値になり、xは1になるので、1==0という比較式になってしまいます。ここでは比較する値が0なので問題は特にありませんが、別の数字と比較する場合に問題になるのでx==?を比較するために( )で先に比較する必要があります。
最初の条件に適合しなかった場合、別の比較式で比較したいという事があります。そう言う場合は、else{ }の中にif文を書いて、さらに適合しなかったらその中のelse{ }にif文を書いて…などとやっているとネストが深くなって読みづらくなります。こういう場合の記入方法があります。
if(x==0) { }else if(y==0){ }else{ }
else の { の前にif文を追加する形の else if という形式です。これを利用することで、階層が深くなるのを防ぐことが出来るようになります。
if(x==1) { }else if(x==2){ }else if(x==3){ }
このような書き方をしてもいいのですが、もう少し便利な方法があるので紹介します。
それがswitch文です。上の例をswitch文に置き換えた物が下のサンプルになります。
switch(x){ case 1: ここに処理を書きます break; case 2: ここに処理を書きます break; case 3: ここに処理を書きます break; }
このようにswitchの隣の括弧の中の変数の状態によってcaseごとに振り分けられます。
xの値が1の場合、case 1:にジャンプします。そしてcase ?とbreak;の間にある処理を実行して、break;命令でswitch文から抜け出します。次はbreak;が無い場合を説明します。
switch(x){ case 1: 処理 case 2: 処理 break; case 3: 処理 }
となっていた場合、case 1にジャンプすると、直下の処理を実行した後に、case 2:の処理も実行し、その後break;によってswitch文を抜け出します。case 2:は普通にbreak;まで実行して終わりです。case 3にもbreak;がありませんが、switch文の終わりの}があるので、case 3:の処理が終わると、そのままswitch文を終わります。
switch文の(x)の中に利用できる変数は整数のみです。
WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int) { try { Application->Initialize(); Application->MainFormOnTaskBar = true; Application->CreateForm(__classid(TForm1), &Form1); Application->Run(); } catch (Exception &exception) { Application->ShowException(&exception); } catch (...) { try { throw Exception(""); } catch (Exception &exception) { Application->ShowException(&exception); } } return 0; }
表記が多少違いますが、C++Builderでも_tWinMainという関数からプログラムが開始されるようになっています。 ここではmain関数以外の関数を自分で作成して利用する方法を解説したあと、既存の標準的な関数をいくつか紹介します。
void func() { Form1->Caption="関数テスト"; }
この関数を書く場所ですが、Unit1.cppの中に、TForm1 *Form1;という部分があると思います。このすぐ下に書き込んでください。そして、フォームにボタンを一つ配置して、ボタンのクリックイベントに以下のように記入してください。
void __fastcall TForm1::Button1Click(TObject *Sender) { func(); }
これで、ボタンを押すと、func()という関数が実行され、タイトルバーの文字が 関数テスト という表示になります。 関数の書き方から見ていきます。まず、void という表記がありますが、これは値を返さない関数の場合に設定する物になります。続いて、funcですが、ここには好きな名前を自由に付けることが出来ます。ただし、名前付けのルールは変数と同じで、数字を先頭に持ってくることは出来ませんし、記号を名前に含めることも出来ません。 次に、( ) がありますが、ここには関数に値を渡したいときに変数名を入力しておくことで、そこを経由して関数に値を送り込むことが出来るようになります。この括弧の中に書かれる、値を受け取る変数のことを引数(ひきすう)と呼びます。では、以下のように入力してください。
int func(int x) { int i; i=x*2; return i; } void __fastcall TForm1::Button1Click(TObject *Sender) { Caption = func(10); }
これは、引数にint型の整数を受けて、その数字を2倍にして返してくる関数の例です。 func(10)は戻り値がint型なので、整数を戻してきます。戻す整数はreturn i;の部分のことで、iの値がCaptionに表示されることになります。
void func1(int x) { x=x*2; } void func2(int *x) { *x=*x*2; } void __fastcall TForm1::Button1Click(TObject *Sender) { int x=10; func1(x); Caption = x; } //--------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { int x=10; func2(&x); Caption=x; }
func1とfunc2の違いは引数にポインタを利用しているかどうかの違いだけです。内容はxの値を2倍するだけですが、func2は正常に動作しますが、func1では実行してもxの値が変化しません。何故かというと、関数の引数に与えられる変数は、ポインタではない場合、引数に入力した変数のコピーが利用されるからです。 コピーが利用されるということは、変数のアドレスが違うということです。なので、コピーをいくら変更しても元のxに何ら変化は起こらないと言うことです。ポインタを置くことで直接アドレスにアクセスできるので、値を編集することが出来るようになります。 引数に変数を渡すことで直接値を編集する書き方がまた別にあります。それが以下の方法です。
void func3(int &x) { x=x*2; } void __fastcall TForm1::Button1Click(TObject *Sender) { int x=10; func1(x); Caption = x; }
こちらでは、引数にint型の普通の変数のアドレスを取得するときのマークが付いています。この方法では、関数内の式にポインタのマークを入れる必要がなく、また、利用するときも、わざわざポインタを渡す必要がありません。 これはどういうことかというと、この引数に普通の変数を渡すと、関数は渡された変数のコピーを作成せず、渡された変数のアドレスを参照し、関数の中では普通の変数の扱いになるので、ポインタのマークが必要なくなります。これをアドレス渡しと言います。
ちなみに、値渡しの場合、メモリ上にコピーを作成するのでサイズの大きな物を渡したり、関数を何度も呼び出す場合は、コピーでメモリを確保するので、動作が非常に遅くなる原因になります。参照回数の多い変数や、大きな変数を利用する時はポインタ渡しかアドレス渡しで渡すようにしましょう。
void __fastcall TForm1::Button1Click(TObject *Sender) { Caption = func(10); } int func(int x) { int i; i=x*2; return i; }
func関数はButton1Click関数の下に作成しました。そして、Button1Click関数の中でfunc関数を呼び出しています。実行してみてください。以下の画像のようなエラーが出たと思います。
利用しようとする関数が自分よりも前に存在しないので、関数が見つからず、未定義の関数としてエラーが出されます。
このようなことが起きてしまうので、利用する関数より前に利用される関数を書く必要があります。
しかし、これを回避する方法があります。次の例を見てください。
int func(int x); void __fastcall TForm1::Button1Click(TObject *Sender) { Caption = func(10); } int func(int x) { int i; i=x*2; return i; }
Button1Click関数の前に、func関数の内容がない名前だけが書かれた行が追加されています。
これは、この宣言をした以降のどこかに、この関数と同じ形式、同じ名前の関数の実態がありますよ。という事をコンパイラに知らせて関数が見つからないということ回避する為のものです。これを、関数のプロトタイプ宣言と言います。
void __fastcall TForm1::Button1Click(TObject *Sender) { char src[]="12345"; char buff[256]; StrCopy(buff,src); Caption=buff; }
StrCopy関数は文字列のコピーに利用する関数で、引数にはコピー先とコピー元のchar型のポインタを指定します。コピー先はあらかじめ、サイズを指定しておく必要があり、ここでは256文字格納出来る配列を作成しました。
StrCopyを実行するとbuffにsrcの内容がコピーされます。表示すると12345がそのまま表示されます。
次にコピーされる時の特徴がよくわかるサンプルを示します。
void __fastcall TForm1::Button1Click(TObject *Sender) { char src1[]="1234567890"; char src2[]="abcd"; char buff[256]; StrCopy(buff,src1); StrCopy(buff,src2); Caption=buff; }
StrCopyを2回実行していますが、最初に実行する方が、長い文字列になっています。
しかしコピーなので、2回目のStrCopyを実行したらbuffに格納される文字列は、abcd になります。
実行してみると、Captionに表示される文字は、abcd だけになっていると思います。
コピーとしては正常にコピーされていると思いますが、コピー先のbuffがどうなっているかというと
abcd_67890_
という形でコピーされています。アンダーバーでNULL文字を表現してみました。見えない文字NULL文字も一緒にコピーされているということです。
確認するためにCaption=buff;の所をCaption=buff+5;として実行してみてください。67890と表示されたはずです。つまり、StrCopyを複数回実行してもbuffの内容の空いている部分がすべて空きに変化するわけではなく、必要な箇所だけがコピーされるということです。Captionに代入するとき、StringクラスはNULL文字までを読み込むので、最初のNULL文字が確認された位置までが読み込まれることになります。
次に、char型の配列の文字列を連結する関数を紹介します。StrCat関数です。C言語標準の関数名ではstrcatです。
void __fastcall TForm1::Button1Click(TObject *Sender) { char src1[]="1234567890"; char src2[]="abcd"; char buff[256]; StrCopy(buff,src1); StrCat(buff,src2); Caption=buff; }
StrCatは最初の引数に先頭に来る文字列配列を指定し、2番目の引数に後ろに接続する文字列配列を指定します。サンプルの実行結果は1234567890abcdになります。StrCatを使うと、最初の引数の文字列配列のNULL文字を除外してそこから後続の文字列を連結します。
最初の引数の配列のサイズは連結後の文字列よりも大きいサイズを用意しなければ行けません。
関数のマニュアルはWeb上で確認できます。ここには書き切れないほどの関数の説明を見ることが出来ます。
URL:http://docwiki.embarcadero.com/Libraries/XE4/ja/System.SysUtils