Posted at

独習C++メモ : 第2章

More than 5 years have passed since last update.


コンストラクタ関数

オブジェクトが作成される時に呼び出される関数で、インスタンスの初期化を行う場所。

コンストラクタ関数はクラス名と同じ名前の関数で、戻り値を持たない。

class MyObject {

int *array;

public:
MyObject(int size);
};

MyObject::MyObject(int size)
{
this->array = (int *)calloc(size, sizeof(int));
}

上の例のように、コンストラクタに何かしらの引数を取って非公開メンバの初期化をするなどが実用的。


C++では変数宣言は実行文

例えばCの int num; は単純に変数を作成しているだけだが、C++での MyObject obj;MyObject 型の変数 obj を作成すると同時にコンストラクタ関数の呼び出し(=インスタンスの初期化)まで行っている。


デストラクタ関数

コンストラクタ関数とは逆に、オブジェクトが破棄される時に呼び出される関数。


オブジェクトが保持しているメモリの開放などを行う。

デストラクタ関数はクラス名の前にチルダ ~ をつけた名前で定義する。

class MyObject {

int *array;

public:
~MyObject();
};

MyObject::~MyObject()
{
free(this->array);
}


継承

第2章では簡単な紹介のみ。

継承とは、既に定義されているクラスの性質を引き継いで、拡張や変更を加えた新しいクラスを定義すること。


継承される側のクラスを基本クラス、継承する側のクラスを派生クラスと呼ぶ。

class ParentClass {

int num;

public:
void setNum(int num);
int getNum();
};

class ChildClass : public ParentClass {
char name[64];

public:
void setName(char *name);
char *getName();
};

public キーワードは、基本クラスの全ての公開要素を派生クラスの公開要素とすることを示す。


基本クラスの非公開要素は非公開のままで、派生クラスから直接アクセスすることはできない。


ドット演算子とアロー演算子

オブジェクトの実体に対してメンバや関数を参照する場合はドット演算子 . を使用する。


一方、オブジェクトのポインタに対して参照する場合はアロー演算子 -> を使用する。

MyObject obj1();

obj1.hoge();

MyObject obj2();
MyObject *obj2_ptr;
obj2_ptr = &obj2;
obj2_ptr->hoge();


C++での構造体

C++では構造体が拡張され、なんとクラスと同様にメンバ関数、コンストラクタ、デストラクタを定義できるようになっている!

struct Hoge {

int a;

private:
double b;
} hoge;

C++では構造体もクラスもどちらも新しいクラスを作成する。


これはCからの上方互換性を維持するためで、Cでは構造体のメンバはデフォルトで公開なので private: キーワードが追加されている。

クラスと構造体どちらを使うかは自由だが、個人的には、クラスはデータとその処理が結びついたものであるはずなので、メンバと関数を保つ場合はクラスを、メンバのみの場合はクラスまたは構造体というスタイルでいきたい。


復習: 共用体

共用体とは、構造体と同じように複数のデータ型をひとつにまとめて扱うためのデータ型。


メンバの中で最も大きなデータサイズと同じ大きさの領域が共用体の領域として確保され、全てのメンバで共有される。


ただしメンバのアドレスは全て同じで、確保されたメモリの先頭である。


共用体の書式は構造体と同じ。

union Union {

int num;
char ch[4];
};

メモリ領域に対して、様々な型で簡単にアクセスできるようにしたものという感じ。


ここでは実用的な例の紹介はしない。


C++の共用体

C++の共用体では、構造体と同じくコンストラクタ・デストラクタが定義できるようになっている。


無名共用体

C++には無名共用体という特別な共用体があり、メンバは同じメモリ領域を共有する。


定義されたメンバは通常の変数と同様に扱われる。

union {

int num;
char ch[4];
};

また、無名共用体にはいくつかの制限が加えられている。

* グローバルな無名共用体は static として宣言しなければならない

* 非公開メンバを含めることはできない

* メンバの名前は同じスコープ内の他の識別子と重複してはいけない


C++の構造体

C++の構造体はタグ名をそのまま型名として使用できる。


Cでは struct キーワードをつけるか typedef しなければならなかった。

Cでの構造体

struct _user {

char name[64];
char email[64];
};
typedef struct _user user_t;

struct _user user_a;
user_t user_b;

C++での構造体

struct user {

char name[64];
char email[64];
};

user user_a;


インライン関数

C++では、実際に呼び出す代わりにインラインで展開する関数を定義できる。

展開することにより、通常の関数呼び出しよりも高速に実行できるというメリットがあるが、インライン関数で大きな処理を定義してしまうとプログラムが大きくなってしまうというデメリットもある。


インライン関数はオーバーロード可能。

inline int max(int a, int b)

{
return (a > b) ? a : b;
}

Cの引数付きマクロとの比較


  • マクロを正しく展開させるためにはカッコ () をつけなければいけないが、インライン関数では気にしなくて良い

  • インライン関数はコンパイラによって最適化される

inline はあくまでコンパイラへの要求であり、何らかの理由でインライン展開できない場合は通常の関数としてコンパイルされる。


また、コンパイラによってはインライン関数に制約が課されている場合があるので注意。


自動インライン化

インライン関数はクラス定義の中に含めることができる。

class MyObject {

public:
inline int max(int a, int b) { return (a > b) ? a : b; };
};

短いメンバ関数はクラス定義に書いてもよさそう。(速度を求めるか、可読性を上げるか?)