TADOConnection

C++BuilderXEでADOConnection使ってSQL Serverに接続してみようとコンポーネントをフォーム上に設置しようとしたら

dcllmdrtlx10_150.bplが共通パスに存在しない

とか怒られたので、探してみたら何故かC:\の直下に落ちてた。
C++Builderのbinにコピーして事なきを得た。

カテゴリー: C++Builder, データベース | タグ: , , | コメントをどうぞ

C++Builder入門:Additionalコンポーネント

第二章:コンポーネントの使い方

2.Additionalコンポーネント

2.1.TBitBtn

TBitBtnはボタンにイメージと文字列を表示できるボタンコンポーネントです。GlyphプロパティにBMP画像を設定すれば、任意の画像をボタンに表示することも出来ます。その際、横に長い画像を使えば、NumGlyphsプロパティで画像を最大4等分に切り分けて、上がっている,下がったまま,使用不可,およびクリックされている。という状態ごとに割り当てることが出来ます。
さらに、Kindプロパティではあらかじめ設定されたボタンの機能と画像が用意されており、これを選択するだけで、コードを書くこと無く、指定の動作をさせることが出来ます。
画像はKindにbkCloseを設定したTBitBtnです。このボタンはOnClickイベントに何も記入していませんが、bkCloseを選択していることで、フォームを閉じる(ここではアプリケーションを終了)動作を自動的に行います。
TBitBtnサンプル

2.2.TStringGrid

TStringGridは文字列を表計算ソフト方式で表示するコンポーネントです。
表示するマスの数はStringGrid1->ColCountで横列の数、StringGrid1->RowCountで縦の行数を設定します。格納される文字列はStringGrid1->Cells[x][y]から個別にアクセス出来ます。その他、StringGrid->Rows[y](横列)StringGrid1->Cols[x](縦列)の2つのプロパティにより、縦横指定の位置の一覧を取得することが出来ます。このRowsとColsプロパティはTStringsの文字列配列なので、ListBox1->Itemsのように簡単にアクセスすることが出来るようになっています。
実行時に文字列を編集したい場合は、オブジェクトインスペクタのOptionを展開し、goEditingにチェックを入れると、実行時にマスごとにテキストを編集することが出来るようになります。
FixedColやFixedRowは表示を固定化する為のもので、固定化したい行数、列数を入力してください。表示が灰色になり、編集や選択も出来なくなります。
次のサンプルを入力すると、下の画像のようになります。

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");

TStringGridサンプル
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以上の数値を指定した場合、空文字が取得されます

カテゴリー: C++Builder, C++Builder入門 | コメントをどうぞ

C++Builder入門:Standardコンポーネント

第二章:コンポーネントの使い方

1.Standardコンポーネント

1.1.TButton

TButtonはボタンをクリックすると、押し下げられたイメージとともにOnClickイベントが発生するコンポーネントです。OnClickイベントに実行したいプログラムを書くことで、ボタンを押したときに入力したプログラムを実行します。
ちなみにOnClickイベントは、ほぼすべてのVCLコンポーネントに搭載されていますが、ボタンをクリックしたときのイメージがないとボタンをクリックした感じが持てない場合もあり、ただクリックするだけのコンポーネントとしては、TButtonがクリックされた表示を見せてくれるようになっているので、これを推薦します。

1.2.TEdit

TEditは文字列を入力、表示するためのコンポーネントです。入力した文字はTEditのTextプロパティに保存され、その保存した文字が表示されます。
TEditの表示
TEditコンポーネントでは1文字入力するごとに発生するイベント、OnChangeイベントがあり、このイベントで、入力された文字をチェックしたりできます。

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にする事で入力できない状態に設定することも出来ます。

1.3.TLabel

TLabelはフォーム上に常に表示させておきたいメッセージなどがある場合に設置します。このコンポーネントにもOnClickイベントなどがあり、クリックしたときにサイトを開くリンクの役割を持たせたりする使い方をされたりします。

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のテスト
このサンプルは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でかまいません。

1.4.TCheckBox

TCheckBoxはマウスでクリックしたり、Checkedプロパティにbool型の値をセットする事で、チェックマークをオンオフする事が出来ます。アプリケーションはこのCheckedプロパティの値に応じて処理を分ける様な仕組みを作ることになります。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    if(CheckBox1->Checked)
    {
         ShowMessage("チェックされています");
    }
}

このサンプルはフォームにTButtonとTCheckBoxを一つずつ設置し、TButtonのクリックイベントでCheckBox1のCheckedプロパティがTrueの場合、メッセージを表示します。(if文は比較式に入れた値が1(True)の場合は条件が成立しているという意味になります。)
TCheckBoxテスト

1.5.TRadioButton

TRadioButtonは複数のTRadioButtonを設置することで、どれか一つだけをチェックするためのコンポーネントです。フォーム上に2つ以上設置してそのまま実行してみてください。必ず一つだけにしかチェックが入りません。
TRadioButtonもTCheckBoxと同じで、Checkedプロパティを参照する事でチェックのオンオフを確認します。
そして、TRadioButtonはコンポーネントの同一階層上の物のうちの一つだけにチェックが入るだけで、複数の階層上に同じように設置すると2カ所でそれぞれチェックのオンオフを設定できるようになります。
下の画像はフォーム上とTPanel上にそれぞれTRadioButtonを複数設置したサンプルです。フォーム上とTPanel上でそれぞれチェックが入っています。
TRadioButtonの表示

1.6.TListBox

TListBoxは複数の文字列を格納して表示するコンポーネントです。文字列を格納するのはItemsプロパティで、このItemsプロパティのAdd関数によりStringに格納された文字列を保存します。
保存された文字列は1行単位で選択出来るようになっており、ListBox1->Items->Strings[行番号]などとして1行の文字列を選択できます。画面上で選択した行はListBox1->ItemIndexというint型のプロパティに保存されています。1行も選択されていないときは-1が入ります。
ItemsプロパティはTStringsという文字列の配列クラスで、このクラスには文字列を追加したり、削除したり出来る機能が備わっています。
次のサンプルはフォームにTListBox、TEdit、TButtonを一つずつ設置して次のコードを入力してください。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
	ListBox1->Items->Add(Edit1->Text);
}

ボタンをクリックするたびにEdit1に入力した文字列をListBox1に格納して表示します。
TListBoxテスト

1.7.TComboBox

TComboBoxはTEditとTListBoxが合わさって1行表示になったListBoxの様な物です。ListBoxと同じく、Itemsプロパティに文字列を格納し、選択した文字列の番号はItemIndexプロパティに格納されます。
TComboBoxはStyleプロパティにより、入力可能なBoxか選択専用のBoxかを選べるようになっています。csDropDownを選べば入力可能かつ文字列をドロップダウン形式で選択できます。csDropDownListを選ぶと選択専用の表示形式になります。csOwnerDraw~という物はプログラマーが独自に表示を描き込む為のオプションで、見た目にインパクトを与えたいときはこちらを利用しますが、これを選択するとプログラマーが表示される文字も自分で書き込まなければいけません。描き込むときは、OnDrawItemイベントを利用します。csSimpleはただのTEditと同じような入力可能なBoxになります。
AutoCompleteプロパティをTrueに設定すると、入力可能状態で入力するとき、Itemsに格納された各文字列の先頭から調べ合致した物が自動的に入力される機能を実装できます。
TComboBoxテスト

1.8.TMainMenu

TMainMenuはフォームにメニュー項目を取り付けるコンポーネントです。フォーム上に設置したTMainMenuのアイコンをダブルクリックすることで、メニュー項目を作成したり削除したりする編集ウインドウが開きます。メニュー項目を削除するときは、編集画面で削除したい項目上で右クリックするとポップアップメニューが開くのでそこから削除を選択してください。
各メニュー項目はOnClickイベントを書き込むことで、そこで機能を実装します。
フォーム上にMainManuを設置すると、Form1のメンバーで、オブジェクトインスペクタで赤字で表示されているManuという部分がMainManu1に設定されます。これによりForm1のメニューが設定されます。
TMainMenu作成サンプル
画像はメニュー項目を作成している途中の画像で、何も文字列が書かれていない所を選択して、オブジェクトインスペクタのCaptionを入力して入力を完了するとメニュー項目が作成されます。作成するメニュー項目はCaptionに日本語を利用した場合、Nameに設定される名前がN~という名前になってしまうので、Captionに日本語を入力したときは、Nameに意味のある名前を付けることをおすすめします。

1.9.TPopupMenu

TPopupMenuはコンポーネント上で右クリックしたときに表示するメニュー項目を作成します。メニューの作り方はTMainMenuと同じで、設置したアイコンをクリックすると編集画面が開きます。
TPopupMenuをフォーム上に配置し、メニュー項目を作成して、例えばForm1でオブジェクトインスペクタのPopupMenuと書かれたところで、PopupMenu1を選択して、実行すると、Form上を右クリックしたときに設定したメニューを開くことが出来ます。TFormにあるPopupMenuというプロパティはほぼすべてのコンポーネントに備わっているので、例えばTButtonなどでもポップアップメニューを表示させることが出来ます。
メニュー項目はCaptionに-を入力するとセパレーターを表示させることが出来ます。
TPopupMenuサンプル

1.10.TPanel

TPanelはレイアウト設定用のコンポーネントです。ほとんどのコンポーネントにもレイアウト用のAlignプロパティはありますが、コンポーネント単体でのレイアウトには限界もあり、そういったとき、TPanelを利用することで、コンポーネントをグループ化して、細かいレイアウト調整が出来るようになります。
TPanelにコンポーネントを配置すると、配置されたコンポーネントはForm1の直下では無く、Panelの下に置かれるので、Panelを非表示にするとPanel上のコンポーネントも一緒に非表示に出来るようになります。
次の画像は、TPanelのAlignプロパティをそれぞれ設定した物です。Panel1はalTop,2はalBottom,3はalLeft,4はalRight,5はClientを設定しています。ウインドウのサイズを広げると、上下左右のPanelはそれぞれ縦横に伸びるだけですが、Panel5はウインドウのサイズが変わると大きさが追随してPanelの下にあるFormが絶対に見えないようになります。
TPanelサンプル

カテゴリー: C++Builder, C++Builder入門 | コメントをどうぞ

C++Builder入門:名前空間

第一章:基礎知識

9.名前空間

9.1.関数の定義と名前空間

これまでのサンプルではクラスの中の定義と同じ位置に関数の内容も書いてきましたが、例えばC++BuilderではTForm1のButton1Click関数を見ると、Unit1.hの方にクラスの定義が、Unit1.cppの方に、関数の内容が書かれています。
このUnit1.hというファイルはヘッダーファイルでこのファイルはUnit1.cppのファイルの上に書かれるようになります。
そのUnit1.cppの上に書くための記述が#include “Unit1.h”という表記で、この表記の位置にUnit1.hのファイルの内容がコンパイル時に自動的に挿入されてからコンパイルされます。
関数の書き方ですが、次の例を見てください。

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コードブロックを利用した名前空間のサンプルです。クラスや構造体とは違い、通常の変数や関数、場合によってはクラスを分類で分けるために利用されます。

9.2.namespaceの使い方

namespace内で宣言した名前の変数は、別の名前空間でも同じ名前の変数や関数を宣言できます。その変数を使うときは名前空間名::変数名という記述をしなければなりません。
そして、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が宣言されていますが、このように名前空間の中に名前空間を収めることも出来るようになっています。

9.3.名前空間の省略表記

名前空間に重複する変数や関数名がない場合、名前空間を書かなくてもいいようにする方法があります。それがusing namespaceという記述です。

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;という記述になります。

カテゴリー: C++Builder, C++Builder入門 | コメントをどうぞ

C++Builder入門:クラス

第一章:基礎知識

8.クラス

8.1.クラスとは

クラスは構造体とほぼ同じような物で、変数や関数の集合体です。オブジェクト指向開発といえばほぼ間違いなく、このクラスが利用されます。
クラスと構造体の違いは、クラスでは、メンバーに対して細かいアクセス制限を掛けることが出来るようになっていることです。
クラスの例を示します。

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として機能するのですが、次で説明する継承によってその動作に違いが出てきます。

8.2.継承

クラスには継承と呼ばれる親子関係のような機能があります。親になるクラスを基底クラス(スーパークラス)と呼び、子になるクラスを派生クラス(サブクラス)と呼びます。次の例を見てください。

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のクラスの宣言が書かれています。
Form1クラスの宣言
C++BuilderではVCLを利用するための独自拡張がされているので、__published:という部分がありますが、ここは統合環境が自動的に記入する部分で、通常この下に書き込むことはありません。
TForm1のprivateやpublicに変数や関数を描き込むことで、TForm1のメンバーとして利用することが出来るようになります。

8.3.継承の宣言

継承の宣言で:public _baseと書きました、このpublicという宣言をprivateと書き換えることも出来ます。
こうすると、継承元のpublicはprivate扱いになり、派生クラスを利用するとき外部から基底クラスのpublicにアクセスすることが出来なくなります。派生クラス内から基底クラスのpublicにはアクセス出来ます。
派生クラスを作成するときにprivateと宣言することで、変化するのは基底クラス内のpublicの扱いがprivateになり、基底クラスのprotectedは、有効範囲を派生させたそのクラスまでに制限されます。privateで派生されたクラスの、さらにそこから派生したクラスからは、親の親のprotectedを見ることは出来ません。

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にアクセスすることは出来ません。

8.4.クラスのメモリ確保

通常クラスは動的にメモリを確保します。その際、mallocは使わず、newを利用します。
これは、malloc free関数では、クラスのコンストラクタとデストラクタが実行されないからです。

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;と、しただけでは
利用できないようになっています。

8.5.コピーコンストラクタ

クラスにメモリを割り当てたとき、クラスはコンストラクタを実行しますこれはクラスの初期化を実行する物ですが、クラス内の変数をその都度、指定の値で初期化してから利用したい場合もあるかと思います。
このとき利用できるのがコピーコンストラクタです。
コピーコンストラクタは次のように宣言し使用します。

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がその値で初期化されます。

8.6.仮想関数

仮想関数とは、基底クラスで宣言した関数を派生先でも必ず宣言させる為の記述のことで、virtualというキーワードを関数の宣言の前に記述します。

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()が実行されるようになります。

補足説明
このbaseSetの(_base *b)がやっていることと、Button1Clickの(TObject *Sender)のやっていることはほぼ同じ事で、このSenderの中にはイベントを発生させたコンポーネントのアドレスが入っています。このことから次のようなことが出来るようになります。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    TButton *btn=dynamic_cast<TButton*>Sender;
    ShowMessage(btn->Name+"が押されました");
}

Button1のOnClickイベントに上のサンプルのように書き、このほかにButton2を用意します。そしてButton2のイベントでOnClickの所のプルダウンメニューを開き、Button1Clickを選択します。
TObject *Senderの説明
これを実行すると、クリックしたボタンの名前を表示するメッセージが表示されるようになります。
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から継承された物だからです。

カテゴリー: C++Builder, C++Builder入門 | コメントをどうぞ

C++Builder入門:構造体

第一章:基礎知識

7.構造体

7.1.構造体とは

これまで、変数の宣言は以下のようにしてきました。

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;

このように、変数名の後ろに.(ドット)演算子を付けることで構造体の中の個別の変数にアクセス出来ます。このときの.は小数は全く関係ないので、アクセスする時のルールとして覚えてください。もちろん数字の後ろに.を付けて数字を書けばそれは小数ですが、変数名の後ろに.を付けたらそれは構造体へのアクセスという意味になります。もちろん構造体以外の変数では使えません。

7.2.構造体のポインタ

構造体も、もちろんポインタ変数が使えます。しかし、ポインタを使うと構造体のメンバーへアクセスする時の演算子が変わります。

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;

7.3.構造体の初期化

構造体では宣言した変数を初期化したいと思っても次のようには出来ません。

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="デストラクタ";
    }
};

デストラクタはコンストラクタと同じく構造体の名前を書きますが、頭に~(チルダ)を付けます。
通常デストラクタではコンストラクタの初期化で確保したメモリを解放する処理を書いたりします。

7.4.構造体の関数

初期化でコンストラクタが関数と書きましたが、つまり構造体には関数を含めることが出来ます。

struct _abc{
    int a;
    int b;
    int func(){
      return a+b;
    }
    _abc(){
        a=10;
        b=5;
    }
};
_abc x;
Caption = x.func();

ここまで説明しましたが、実際の所、構造体で関数を駆使して使用するというシーンはほとんどありません。それは何故かというと、クラスが構造体の上位版とも言える機能を持っているからで、C++の機能とは言え、クラスの概念はほぼすべての言語で利用されています。構造体でもここまでは出来る程度に覚えておいてください。

構造体は通常、ファイルを読み込むとき、ファイルフォーマットに合わせた宣言をすることで、簡単に情報を読み込むという使い方がされるのが一般的です。
カテゴリー: C++Builder, C++Builder入門 | コメントをどうぞ

C++Builder入門:変数2

第一章:基礎知識

6.変数2

6.1.メモリの確保

これまで、変数はint x;やchar c[]=”abc”;などとして宣言してきました。ここではわかりやすいのでchar型を例に説明をします。
これまでのchar c[]=”abc”;やchar c[256];としてメモリを確保する事を静的確保と言います。
静的確保は長さをあらかじめ決めてから宣言する方法です。
配列に文字列を連結する関数StrCatでは、コピー先のchar型の配列の長さは、連結後の長さ以上のメモリを確保しなければなりませんでした。先の例ではchar c[256];などとしてあらかじめこれ以上長くなることは無い。というサイズを指定してメモリを確保していました。しかし、これではメモリがもったいない上に、予期せぬ長さの文字列が来た場合エラーになってしまいます。
ここでは長さを利用するサイズだけ確保する動的確保を説明します。

6.2.動的確保 new と malloc

C++言語ではメモリの確保にnewとmallocという物を用いてメモリを確保することが出来ます。
newの方はC++言語で利用できる物で、mallocはC言語で利用できる物です。C++はCの上位互換なのでmallocも使えます。
何故C++には新たにnewが追加されたのかという話は、後述するクラスが関係しています。今は2種類の方法があるとだけ覚えておいてください。

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;という書き方になります。

補足説明
malloc関数の頭に(char*)という物が付いています。これは、キャストという型変換の命令を与えています。この場合、malloc関数の戻り値が、void型のポインタで、これは型が決まっていませんという意味です。そこで、この場合、char型のポインタにする必要があるので、char*を先頭に付けています。しかしこのままでは、*が右側に付いているので、演算記号として認識してしまうので、括弧で囲うことで演算記号では無いという意味合いを持たせてあります。
キャストの例
char c=’a';
int i;
i=(int)c;
char型は実は文字型とは言っても中身に文字を格納しているわけでは無く、-128~127の数字を格納しています。なので、int型にキャストする事が出来ます。逆にint i=300;をchar型にキャストすると数値は44になります。
int i=300;
char c;
c=(char)i;
Caption=(int)c;
これは、char型が格納出来る範囲が最大256までという理由で、最大格納サイズより大きな数値が入ってくると、格納出来るように0~127を超えると-128に戻り、そこからさらにカウントして44になります。(300-256=44)
では何故char型が文字型と呼ばれているかというと、文字を表示するときパソコンは文字コード表という物を持っていて、あらかじめ決められた数字を指定の方法で送ると、その番号に割り当てられた文字を表示するという機能を持っています。char型は最低限のアルファベットと記号を表示するのに十分なサイズがあったので文字型として使われています。文字コード表には種類があり、Shift-JISやUTF8、Unicodeなどがあります。

カテゴリー: C++Builder, C++Builder入門 | コメントをどうぞ

C++Builder入門:繰り返し処理

第一章:基礎知識

5.制御文

5.3.繰り返し処理 – for

プログラムでは同じ処理を何度か実行したいという場合があります。C言語には繰り返し文という物がいくつかあります。そのうちのfor文を説明します。

int x=0;
for(int i=1;i<10;i++)
{
   x=x+i;
}
Caption = x;

このサンプルは1~9を足した物をxに代入しています。
まず文法の説明をします。
for()この括弧の中は 初期値 比較式 制御用の式 が入ります。
for( 初期値 ; 比較式 ; 制御用の式 )
それぞれは;セミコロンで区切ります。
比較式で利用する初期値をまず設定します。初期値は0以外でも数字なら何でもかまいません。そして比較式を書きます。この比較式の条件が成立している間ループすることになります。そして、条件を抜けるために初期値の変数の値を上下させる制御式を書きます。
比較式が成立している間{ }の中(コードブロック)の処理が繰り返されることになります。

補足説明
i++という式が出てきました。これはインクリメントといって、対象の変数に1を加算するという意味です。逆に1を減算する事をデクリメントといい、i– 変数名マイナスマイナスという書き方になります。
このほかにも式を短く書く方法があります。
+= -= *= /=
それぞれ代入先の変数の値に対して代入元の変数の値と計算した上で、値を代入します。
x=5;
x+=10;
xは15になります。

次のサンプルはフォームをクリックして、オブジェクトインスペクタのイベントタブを開き、OnPaintと書かれた部分の右の空白をダブルクリックして作成された関数の中に書き込んでください。
オブジェクトインスペクタ、OnPaint

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命令だけであれば{ }を省略できるという仕様があるので問題はありません。

5.4.繰り返し処理 – while

次に同じく繰り返し処理のwhile文を紹介します。次のように書きます。

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なので条件が成立していないのでコードブロック内の処理は一切行われません。

5.5.繰り返し処理 – do – while

while文は最初に比較式を調べて条件が成立していなければ一度も実行されませんでした。次に紹介するdo ~ whileはコードブロック内の処理が最低1回は必ず行われる繰り返し処理になります。

int x=10;
do{
    x++;
    Caption=x;
}while(x<10);

まずxが10で初期化されています。そしてdo whileのコードブロックが実行され1が加算され、タイトルバーに現在の値を表示します。その後、x<10で比較して成立していないので終了します。

カテゴリー: C++Builder, C++Builder入門 | コメントをどうぞ

C++Builder入門:条件分岐

第一章:基礎知識

5.制御文

5.1. 条件分岐1  -  if

ある変数の値が指定の数字の場合に処理を分けたいという場合があると思います。そこで利用されるのがif文です。文法は次の通りです。

int x=0;
if(x==0)
{
  x=1;
}else{
  x=0;
}

if文は if(条件){成立したときの処理}else{成立しなかったときの処理} という文法で入力します。条件は、比較式を入力します。比較式で利用する記号を比較演算子と呼びます。

== 左右の値が同じ
!= 左右の値が違う
< 左の値が右の値より小さい
> 左の値が右の値より大きい
<= 左の値が右の値より小さいか同じ
>= 左の値が右の値より大きいか同じ

これらの比較演算子を使って条件を組み立てます。
上のサンプルの例では、xが0かどうかを調べています。サンプルではxは常に0なので、成立しなかった場合の処理(else)は実行されません。xが0の場合の処理だけを書きたい場合は、else以降は記入しなくても問題ありません。

補足説明
比較演算を実行すると、成立していればbool型の値でtrueが出力され。成立していなければfalseが出力されます。
bool型とはfalseかtrue(0かそれ以外)を表現する変数です。VCLコンポーネントでは機能のオンオフによく利用されています。

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==?を比較するために( )で先に比較する必要があります。

補足説明
比較式用の論理演算は && || !があり、これらの演算子は0か1しか演算しません。
&&は、1&&1 の時のみ1を返す
||は、0||0 の時のみ0を返す
!は、演算結果を反転させる(0以外を0に、0を1にする)

最初の条件に適合しなかった場合、別の比較式で比較したいという事があります。そう言う場合は、else{ }の中にif文を書いて、さらに適合しなかったらその中のelse{ }にif文を書いて…などとやっているとネストが深くなって読みづらくなります。こういう場合の記入方法があります。

if(x==0)
{

}else if(y==0){

}else{

}

else の { の前にif文を追加する形の else if という形式です。これを利用することで、階層が深くなるのを防ぐことが出来るようになります。

5.2.条件分岐2 – switch

ある変数の値がいくつかの値の場合に処理を分けるにはif文では条件ごとにif文を用意しなければなりませんでした。以下の例はxの値が1か2か3の時に処理を分ける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)の中に利用できる変数は整数のみです。

カテゴリー: C++Builder, C++Builder入門 | コメントをどうぞ

C++Builder入門:関数

第一章:基礎知識

4.関数

4.1.関数とは

C言語では関数と呼ばれる一つの処理をまとめる機能があります。これを利用することで、任意の処理を実行したり、変数の値を編集したりできます。 C言語では main関数からプログラムが開始されるというルールがあります。C++Builderでも同じように出来ていて、プロジェクトマネージャのProject1.cppというファイルをダブルクリックして開いてみてください。次のようになっていると思います。

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関数以外の関数を自分で作成して利用する方法を解説したあと、既存の標準的な関数をいくつか紹介します。

4.2.関数の基本

単純な関数の例を示します。これが基本的な関数の書き方になります。

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型の普通の変数のアドレスを取得するときのマークが付いています。この方法では、関数内の式にポインタのマークを入れる必要がなく、また、利用するときも、わざわざポインタを渡す必要がありません。 これはどういうことかというと、この引数に普通の変数を渡すと、関数は渡された変数のコピーを作成せず、渡された変数のアドレスを参照し、関数の中では普通の変数の扱いになるので、ポインタのマークが必要なくなります。これをアドレス渡しと言います。

補足説明
引数にコピーを作成させる方法を値渡しといい、その他すべてをアドレス渡しと、言う人も居るらしいのですが、ポインタを引数に付けている場合と、アドレス参照のマークを付けている場合では、渡す変数が、ポインタであるか、実態であるかの違いがあるので、私の場合はポインタの場合はポインタ渡し、実態を渡す場合はアドレス渡しと呼び分けています。 ちなみにC++Builderでは、コンポーネントの多くがポインタ渡しを利用しています。その理由は、後述するクラスで説明します。

ちなみに、値渡しの場合、メモリ上にコピーを作成するのでサイズの大きな物を渡したり、関数を何度も呼び出す場合は、コピーでメモリを確保するので、動作が非常に遅くなる原因になります。参照回数の多い変数や、大きな変数を利用する時はポインタ渡しかアドレス渡しで渡すようにしましょう。

4.3.関数の宣言位置

関数を宣言するには利用する場所よりも前に書かなくてはいけないというルールがあります。次の通りに入力してください。

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関数の内容がない名前だけが書かれた行が追加されています。
これは、この宣言をした以降のどこかに、この関数と同じ形式、同じ名前の関数の実態がありますよ。という事をコンパイラに知らせて関数が見つからないということ回避する為のものです。これを、関数のプロトタイプ宣言と言います。

4.4.色々な関数

それではC++Builderに標準的に搭載されている関数を紹介します。
まずは、char型配列に文字列を書き込むstrcpy関数ではなく、StrCopy関数を紹介します。
実は、strcpyはC言語で標準的に用意されている汎用の関数なのですが、StrCopyはC++BuilderのVCLで標準的に用意されているstrcopyと同等の関数になっています。VCLは元々C++Builderの兄貴分のDelphi用のライブラリで、この関数もDelphi用に用意された関数です。今回はせっかく用意されているのでVCLのStrCopyを利用します。

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

カテゴリー: C++Builder, C++Builder入門 | コメントをどうぞ