第7回

ヘッダファイルとcppファイル

クラスを複数ファイルに分割する。

なぜ分割するのか

すべてをmain.cppに書くと、

main.cpp
 ├── LED
 ├── Motor
 ├── Sensor
 ├── Controller
 └── ...

となり、コードが巨大になる。そこで、

main.cpp
LED.hpp
LED.cpp
Motor.hpp
Motor.cpp

のように分ける。

宣言と定義

LED.hpp
class LED {
public:
    LED(int pin);

    void on();
    void off();

private:
    int pin;
};
LED.cpp
#include "LED.hpp"

LED::LED(int pin)
    : pin(pin) {
}

void LED::on() {
}

void LED::off() {
}

include

#include "LED.hpp"
書き方意味
#include "LED.hpp"自分のプロジェクト内のファイル
#include <Arduino.h>処理系やライブラリが提供するファイル

ヘッダガード

#ifndef LED_HPP
#define LED_HPP

// ...

#endif  // LED_HPP

または、

#pragma once

を紹介する。同じヘッダが2回読み込まれて多重定義になるのを防ぐ仕組みである。

宣言と定義の考え方

.hpp
↓
「何ができるか」

.cpp
↓
「どうやって実現しているか」

という考え方を持たせる。利用する側は.hppだけを見れば使い方が分かる、という状態が理想である。

動かして確かめる

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

code/examples/07-led-header/
├── include/
│   └── LED.hpp      宣言(何ができるか)
└── src/
    ├── LED.cpp      定義(どうやって実現しているか)
    └── main.cpp     使うだけ
code/examples/07-led-header/src/main.cpp
// 第7回:main.cpp は「使うだけ」になる

#include <Arduino.h>

#include "LED.hpp"

LED led(LED_BUILTIN);  // ボード上のRGB LED。配線なしで光る

void setup() {
    led.begin();
}

void loop() {
    led.toggle();
    delay(500);
}

RGB LEDが0.5秒ごとに切り替わる。第6回のサンプルと動きは同じで、ファイルの分け方だけが違う。

確かめること。

  • main.cppにLEDの中身が1行も書かれていない。#include "LED.hpp"だけで使えている
  • LED.hppを見れば使い方が分かり、LED.cppに実現方法が書いてある
  • LED.cppのLED::を1か所消すと、メンバ関数ではない「ただの関数」になる。どんなエラーになるかを見ておく
  • LED.hppの#pragma onceを消しても、この構成ではまだ動く。何をしているヘッダなのかを確認する

演習:以下のクラスをそれぞれ.hppと.cppに分割する

LED
Motor
Button
  • ヘッダガードを必ず付ける
  • main.cppから3つすべてを#includeして使う
  • .hppを見ただけで使い方が分かるかを確認する