第5回

C++基礎

大規模なプログラムを整理するためのC++の機能を押さえる。

CとC++

C++はCを基礎として発展した言語だが、C++にはCにはない多くの機能がある。本講座では特に「大規模なプログラムを整理するための機能」に重点を置く。

const

const int maxSpeed = 100;

変更してはいけない値を明示する。うっかり書き換えようとするとコンパイル時にエラーになるため、バグを早い段階で発見できる。

const参照

void print(const Sensor& sensor) {
}

コピーを避けながら変更しないことを表現できる。オブジェクトを引数に渡すときの基本形として覚えておくとよい。

名前空間

namespace Motor {
    void start();
}

使用:

Motor::start();

名前(変数・関数名)の衝突を避け、どのまとまりに属する機能なのかを示せる。Motor::start()とSensor::start()はそれぞれ異なる名前空間に属するため、名前の衝突を避けられる。

関数オーバーロード

int add(int a, int b);

float add(float a, float b);

同じ関数名でも引数の型や数が異なれば複数定義できる。呼び出す側は型を意識せず同じ名前で呼べる。

動かして確かめる

演習に入る前に、まずこのサンプルを動かして結果を見る。追加の部品は要らない。光るのはボードに載っている内蔵LEDで、それ以外はシリアルモニタの出力で確かめられる。code/examples/05-cpp-basics/を開き、pio run -t uploadで書き込んでからpio device monitorで出力を見る(手順と、外付けのLEDなどをつなぐ場合は開発環境にまとめてある)。

code/examples/05-cpp-basics/src/main.cpp(抜粋)
/**
 * @brief 整数どうしの加算
 * @param a 左辺
 * @param b 右辺
 * @return 和
 */
int add(int a, int b) {
    return a + b;
}

/**
 * @brief 小数どうしの加算
 * @param a 左辺
 * @param b 右辺
 * @return 和
 */
float add(float a, float b) {
    return a + b;
}

/**
 * @brief 測定結果を表示する
 * @details const参照で受けるのでコピーが起きず、中身を書き換える心配もない。
 * @param reading 測定結果
 */
void print(const Reading& reading) {
    Serial.print("temp=");
    Serial.print(reading.temperature);
    Serial.print(" humi=");
    Serial.println(reading.humidity);
}

void setup() {
    Serial.begin(115200);
    pinMode(LED_PIN, OUTPUT);
    delay(1000);

    Serial.println("--- 名前空間 ---");
    Sensor::begin();
    Motor::begin();

    Serial.println("--- オーバーロード ---");
    Serial.print("add(1, 2)       = ");
    Serial.println(add(1, 2));          // int 版が呼ばれる → 3
    Serial.print("add(1.5f, 2.5f) = ");
    Serial.println(add(1.5f, 2.5f));    // float 版が呼ばれる → 4.00

    // TEMP_THRESHOLD = 25.0f;  // const なのでコンパイルエラーになる
}
同じファイルの loop()
void loop() {
    Reading reading = Sensor::read();
    print(reading);

    if (reading.temperature >= TEMP_THRESHOLD) {
        digitalWrite(LED_PIN, HIGH);
    } else {
        digitalWrite(LED_PIN, LOW);
    }

    delay(1000);
}
シリアルモニタの出力
--- 名前空間 ---
Sensor::begin()
Motor::begin()
--- オーバーロード ---
add(1, 2)       = 3
add(1.5f, 2.5f) = 4.00
temp=29.50 humi=55.00
temp=31.00 humi=55.00
temp=32.50 humi=55.00
temp=34.00 humi=55.00

確かめること。

  • Sensor::begin()とMotor::begin()が別のものとして呼べている。名前空間が違えば同じ名前でも衝突しない
  • add(1, 2)は3、add(1.5f, 2.5f)は4.00。呼ぶ側は同じ名前で書いていて、型に応じた関数が選ばれている
  • setup()の最後にあるTEMP_THRESHOLD = 25.0f;のコメントを外すとコンパイルエラーになる。constが書き換えを止めている
  • print()はconst Reading&で受けているので、コピーも書き換えも起きない
  • 温度が30.00を超えた周でLEDが点く。しきい値がconstの1行にまとまっている

演習:小規模プログラム

  • constでピン番号やしきい値を定義し、マジックナンバーを排除する
  • 名前空間にセンサ関連の関数をまとめてみる
  • add(int, int)とadd(float, float)を両方定義し、呼び分けを確認する