Index

HOME > プログラムTOP > C++



C++の壺・クラス篇3

 C++の基本テクニックのTipsです。
 クラス篇3では、クラスの特殊な機能と、それらの使用方法を扱います。いずれも使用頻度は少ないと思いますが、プログラミングの効率化に大きな効果を発揮します。
ページ項 目内 容
クラス篇1 クラスの基礎 クラスの宣言 / アクセス修飾子 / コンストラクタ / デストラクタ / メンバ / 静的メンバ / クラスの実装 / thisポインタ
クラスの継承 継承(派生) / メンバ / コンストラクタ / デストラクタ / オーバーライド / 仮想関数 / 抽象クラス
動的インスタンス クラスの実体の動的な作成と破棄
クラス篇2 friendキーワードfriendクラス / friend関数
constキーワード constオブジェクト / constメンバ関数 / constメンバ定数
演算子オーバーロード 演算子のオーバーロード / オーバーロード可能な演算子
クラス篇3 クラスの多重継承 複数のクラスを継承するクラスです
メンバ関数のinline化 処理速度を速める関数です
クラス・テンプレート 様々なデータ型を扱えるクラスです

sasaraan programming

Exposition

■クラスの多重継承

 クラスは、複数のクラスを継承することができます。継承先のクラス名に続いて、" , " で区切って継承元のクラス名を宣言します。この時、継承可能なメンバ(protected, public)は、すべて継承先のクラスで使用することができます。
// 多重継承
#include <stdio.h>    // printf
#include <conio.h>   // getch

// 継承元クラス1 : Pointクラス
class Point
{
public:
    int left;
    int top;
    Point() {printf("start Point\n");}
    ~Point() {printf("end Point\n");}
};

// 継承元クラス2 : Sizeクラス
class Size
{
public:
    int width;
    int height;
    Size() {printf("start Size\n");}
    ~Size() {printf("end Size\n");}
};
// 継承先クラス : Regionクラス
class Region:public Point, public Size
{
public:
    Region() {printf("start Region\n");}
    ~Region() {printf("end Region\n");}
};

// main 関数
void main() 
{
    Region* p;
    p = new Region;

    p->left = 60;       // Pointのメンバ
    p->top = 40;       // Pointのメンバ
    p->width = 480;   // Sizeのメンバ
    p->height = 360;  // Sizeのメンバ

    delete p;

    if (getch()) return;
} 
 オブジェクトが作成される時、コンストラクタは継承元のものも呼ばれます。上記の例では、
  Pointクラスのコンストラクタ → Sizeクラスのコンストラクタ → Regionクラスのコンストラクタ
が順に呼び出されます。また、デストラクタも同様ですが、こちらは順番が逆です。上記の例では、
  Regionクラスのデストラクタ → Sizeクラスのデストラクタ → Pointクラスのデストラクタ
の順で呼ばれることになります。

■メンバ関数のinline化

 通常、関数実行の際には、関数を呼び出す時間がかかります。インライン関数を使うと、このロスを防ぐことができます。ただし、この場合、関数は実行場所に直接埋め込まれるので、プログラムの量は実際の記述よりもかさんでしまいます。現実的には、一行か二行程度の関数に適用させることがほとんどです。
 インライン関数を指定する場合は、関数の最初に inline を記述します。また、クラスの宣言時に同時に実装コードを記述すると、自動的にインライン関数として扱われます。なお、インライン関数は必ず実行されるわけではありません。
// メンバ関数のインライン化
#include <stdio.h>     // printf
#include <stdlib.h>    // rand
#include <time.h>     // time
#include <conio.h>   // getch

// 通常のメンバ関数の実装
class Random
{
public:
    int GetNum(int len);
};

// GetNum関数
int Random::GetNum(int len) {
    return rand() % len;
}
// インライン関数を明示して実装
class Random
{
public:
    int GetNum(int len);
};

// GetNum関数
inline int Random::GetNum(int len) {
    return rand() % len;
} 
// インライン関数として定義
class Random
{
public:
    int GetNum(int len) {
        return rand() % len;
    }
}
// main 関数
void main() 
{
    Random* p = new Random;

    clock_t t1 = clock();    // 時間1

    int r;
    int i = 0;
    while (i < 10000000) {
        r = p->GetNum(100);
        i++;
    }

    clock_t t2 = clock();    // 時間2
    printf("%d\n", t2-t1);  // 経過時間

    delete p;
    if (getch()) return;
}  
 上記は、関数を1000万回実行させた時のケースです。経過時間(tick数)は、筆者の環境では、通常では 966、インライン化した時は 922 (44tick分の向上)を示しました。よほどのパフォーマンスが要求される時以外は、あまり気にかける必要はないかもしれません。

■クラス・テンプレート

 クラス・テンプレートを使うと、ひとつのクラスで、複数のデータ型を扱えるようになります。下記のクラスは、単に足し算と引き算を行うだけのクラスです。左側が通常の定義と実装ですが、int 型を指定する必要があるため、他のデータ型を使用することができません。これを右側のようにテンプレート化すると、他のデータ型の代入が可能となります。
 なお、クラス・テンプレートには既定のコンストラクタ / デストラクタがありません。new / delete を使用する可能性がある時は、コンストラクタとデストラクタを用意しておく必要があります。
// 通常のクラスの定義と実装
#include <stdio.h>     // printf
#include <conio.h>    // getch

// Calcクラス
class Calc
{
public:
    int Plus(int a, int b);
    int Minus(int a, int b);
};

int Calc::Plus(int a, int b) {
    return a + b;
}

int Calc::Minus(int a, int b) {
    return a - b;
}

// main 関数
void main() {
    Calc *p = new Calc;
    int n;

    n = p->Plus(5, 2);      // = 7
    printf("%d\n", n);
    n = p->Minus(5, 2);    // = 3
    printf("%d\n", n);

    delete p;
    if (getch()) return;
} 
// クラス・テンプレートの定義と実装
#include <stdio.h>   // printf
#include <conio.h>  // getch

// Calcクラス
template <typename _t> class Calc
{
public:
    Calc();
    ~Calc();
    _t Plus(_t a, _t b);
    _t Minus(_t a, _t b);
};

// コンストラクタ
template <typename _t> Calc<_t>::Calc() {
}

// デストラクタ
template <typename _t> Calc<_t>::~Calc() {
}

// メンバ関数 Plus
template <typename _t>
_t Calc<_t>::Plus(_t a, _t b) {
    return a + b;
}

// メンバ関数 Minus
template <typename _t> 
_t Calc<_t>::Minus(_t a, _t b) {
    return a - b;
} 
 クラス・テンプレートを定義・実装する際は、上記のように、テンプレートであることを示すため、先頭に " template<一時的に使用するデータ型名> "を記述します。データ型名はプログラマの任意となります。また、クラス名の直後には、実際のデータ型を示す引数の記述も必要となります。
 下記は、上記テンプレートを利用した例です。左側は整数型、右側は浮動小数点型を指定しています。
// クラス・テンプレートの使用1
// main 関数
void main() 
{
    // int型対応のCalcクラス
    Calc<int>* p = new Calc<int>;

    int n;
    n = p->Plus(5, 2);      // = 7
    printf("%d\n", n);
    n = p->Minus(5, 2);    // = 3
    printf("%d\n", n);

    delete p;
    if (getch()) return;
}
// クラス・テンプレートの使用2
// main 関数
void main() 
{
    // float型対応のCalcクラス
    Calc<float>* p = new Calc<float>;

    float f;
    f = p->Plus(5.5, 2.2);     // = 7.7
    printf("%f\n", f);
    f = p->Minus(5.5, 2.2);   // = 3.3
    printf("%f\n", f);

    delete p;	
    if (getch()) return;
} 

www.sasaraan.net

(c) morijoh