C++実践編(カウンタークラスを作成する)

こんにちは、ぷないしんです。

 

昨日の関数の渡し方の詳しい話はまた今度にして今日は、C++のクラスを作成していきたいと思います。

 

 

今回作成するのはズバリ

【カウンタークラス】

内容としては

・0~9の1桁の整数値を数え上げる

・初期化、生成すると同時にカウンタを0にする。

・カウントアップ(値をインクリ)

・カウントダウン(値をデクリ)

・カウンタの値を調べる

 

今回のクラスは小規模なのですべてのメンバ関数をインライン関数にし、ヘッダだけで実現してみます。

 

ということでさっそくコードを記述します。

 

【counter.h】

//カウンタクラス

#ifndef ___Class_Counter
#define ___Class_Counter

#include <limits>

class Counter{
    unsigned cnt;   //カウンタ

public:
    //コンストラクタ カウンタを0に
    Counter() : cnt(0){}

    //カウントアップ (カウンタの上限はunsigned型の最大値になる)
    void increment(){
         if (cnt < std::numeric_limits<unsigned>::max()) cnt++;
    }
    
    //カウントダウン(カウンタの加減は0)
    void decrement(){
        if (cnt > 0) cnt--;
    }

    //カウンタの返却(cntのゲッター)
    unsigned value(){
        return cnt;
    }
};

#endif

 

カウンタを格納しているのが非公開のデータメンバcnt

型はunsigned

 すなわち、カウンタとして表現できる数値はunsigned型で使える範囲と同じです。

ということは加減が0で上限がnumeric_limits<unsigned>::max()になります。

 

 

 

numeric_limits<unsigned>::max()

について

 

まずunsigned型についてのおさらいですが、unsigned型とは

2バイトまた4バイトの符号なし整数の値を記憶できる型です。

 

次にnumeric_limitsですがこれはまず最初に<limits>ヘッダーをインクルードした時に使える標準ライブラリで::maxを使用すればその直前に指定した型の最大値を取得できます。

 

なので今回はunsigned型の最大値を取得するクラスととりあえず理解しておきます。

 

 

次に各メンバ関数の解説をしておくと

 

・コンストラクタ カウンタの値を0にしています。

increment関数 if文でカウンタの値が最大値より小さい時、カウンタの値をインクリメントします。

decrement関数 if文でカウンタの値が0より大きい時、カウンタの値をデクリメントします。

value関数   カウンタの値を返却しています。

 

それではCounterクラスを実際に利用してみましょう。

 

#include <iostream>
#include "Counter.h"

using namespace std;

int main()
{
    int no;
    Counter x;

    cout << "Current counter value:" << x.value() << "\n";

    cout << "Count up count:";
    cin >> no;

    for (int i = 0; i < no; i++){
        x.increment();              //Count up
        cout << x.value() << "\n";
    }

    cout << "Count down count:";
    cin >> no;

    for (int i = 0; i < no; i++){
        x.decrement();
        cout << x.value() << "\n";
    }
}

 

f:id:punainen:20210317154520p:plain

実行結果(赤枠はキーボードより入力)

 

まず最初に、CounterのオブジェクトとしてXを生成、カウンタの値を表示します。

オブジェクト生成時にカウンタが0になっています。

生成後は、キーボードからint形の変数noに入れた回数だけ、メンバ関数incrementによるカウントアップを行いながら値を表示します。

それが終わると、もう一度キーボードからint形変数noに入れた数値の回数だけカウントダウンと値の表示を行います。

 

 

もちろんこのまま利用することも出来るのですが、改良点がいくつかあります。

ユーザー定義型であるクラスCounterを使う時は、値を調べる、カウントアップ/ダウン(インクリ/デクリ)を行うためにvalue関数、increment関数、decrement関数を呼び出す必要があります。

 

int形やlong形の組み込み型と比較すると

・タイプ数が増える→タイプミスしやすくなる

 ・プログラムが冗長になる→読みにくくなる

といったデメリットがあります。

 

クラス型オブジェクトに対してインクリやデクリを行う演算子を使えれば、int形やlong形と同じ感じで利用できるはず!

 

という事で改良していきましょう!

 

NewCounter.h

//カウンタクラス

#ifndef ___Class_Counter
#define ___Class_Counter

#include <limits>

class Counter{
    unsigned cnt;   //カウンタ

public:
    //コンストラクタ カウンタを0に
    Counter() : cnt(0){}

    //unsigned型へ変換する
    operator unsigned() const {
        return cnt;
        }

    bool operator!() const {
         return cnt == 0;
         }

    //前置増分演算子++
    Counter& operator++(){
         if (cnt < std::numeric_limits<unsigned>::max()) cnt++;
         return *this;
    }

    //後置増分演算子++
    Counter operator++(int){
        Counter x = *this;
        ++(*this);
        return x;
    }

    //前置減分演算子--
    Counter& operator--(){
        if (cnt > 0) cnt--;
        return *this;
    }

    //後置減分演算子--
    Counter operator--(int){
        Counter x = *this;
        --(*this);
        return x;
    }
};

#endif

 

 前置きの演算子と後置きの演算子を二つ用意しました。

では早速利用してみましょう。

 

countertest.cpp

#include <iostream>
#include "NewCounter.h"

using namespace std;

int main()
{
    int no;
    Counter x;
    Counter y;

    cout << "Count up count:";
    cin >> no;

    for (int i = 0; i < no; i++)
        cout << x++ << " " << ++y << "\n";
    

    cout << "Count down count:";
    cin >> no;

    for (int i = 0; i < no; i++)
        cout << x-- << " " << --y << "\n";
    
    if(!x)
        cout << "x is 0\n";
    else
        cout << "x is not 0\n";
}

 

f:id:punainen:20210317163312p:plain

実行結果(赤枠はキーボードより入力)

xには後置き、yには前置きの演算子を使いました。

ちゃんと使い分けられてますね。

 

変換関数と演算子関数を定義したので組み込み型と同じ感じでCounterクラスを使えるようなりました。

最初のプログラムと比べてみると使いやすい上に利用する時に簡単ですし読みやすくなりましたね。

 

変換関数や演算子の定義の方法はまた次回として今日はこのあたりにしておきます。

 

ありがとうございました。

 

ぷないしん