第10回
継承
共通する性質を親クラスにまとめ、個別の機能を子クラスに実装する。
継承なし
class IMU {
public:
void update();
};
class GPS {
public:
void update();
};両方にupdate()が存在する。共通点がコード上どこにも表現されていない。
共通部分をSensorへ
class Sensor {
public:
void update();
};class IMU : public Sensor {
};class GPS : public Sensor {
};「is-a」の関係
IMU is a Sensor GPS is a Sensor
継承を使うべきか判断する際の重要な考え方。「AはBの一種である」と自然に言えないなら、継承ではなく「持つ(has-a)」関係を検討する。
protected
class Sensor {
protected:
int value;
};派生クラスから利用できる。外部には見せたくないが、子には使わせたいデータに使う。
動かして確かめる
演習に入る前に、まずこのサンプルを動かして結果を見る。追加の部品は要らない。光るのはボードに載っている内蔵LEDで、それ以外はシリアルモニタの出力で確かめられる。code/examples/10-inheritance/を開き、pio run -t uploadで書き込んでからpio device monitorで出力を見る(手順と、外付けのLEDなどをつなぐ場合は開発環境にまとめてある)。
code/examples/10-inheritance/
├── include/
│ ├── Device.hpp 共通部分(pin / begin / getPin)
│ ├── LED.hpp Device を継承
│ └── Motor.hpp Device を継承
└── src/
├── Device.cpp
├── LED.cpp
├── Motor.cpp
└── main.cpp// 第10回:共通部分を親クラスにまとめる
#pragma once
class Device {
public:
/**
* @brief コンストラクタ
* @param pin 使用するピン番号
* @note explicit は「引数1つのコンストラクタを勝手な型変換に使わせない」指定。
* 付けておくと Device device = 5; のような書き方を防げる。
*/
explicit Device(int pin);
/**
* @brief ピンを初期化する
* @details LED でもモータでも手順は同じなので、親に置ける。
*/
void begin();
/**
* @brief ピン番号を返す
* @return ピン番号
*/
int getPin() const;
protected:
// 子クラスからは使わせるが、外部には見せない
int pin;
};// 第10回:共通部分を親にまとめ、固有の機能を子に置く
//
// Device
// ├── LED
// └── Motor
//
// begin() と getPin() は Device に1つだけ書いてある。
// LED も Motor もそれを書き直していない。
#include <Arduino.h>
#include "LED.hpp"
#include "Motor.hpp"
LED led(LED_BUILTIN); // ボード上のRGB LED
Motor motor(24); // モータはつないでいない。結果はシリアルの出力で見る
void setup() {
Serial.begin(115200);
delay(1000);
// どちらも Device から継承した begin() を呼んでいる
led.begin();
motor.begin();
// getPin() も Device に1つだけ書いてある。
// LED 側は LED_BUILTIN(内蔵RGB LEDを指す特別な値)なので表示しない
Serial.print("motor pin = ");
Serial.println(motor.getPin());
// 固有の機能は子クラスにある
motor.setSpeed(60);
motor.setSpeed(500); // 丸められて 100 になる
}
void loop() {
led.toggle();
delay(500);
}Device::begin()
Device::begin()
motor pin = 24
Motor pin=24 speed=60
Motor pin=24 speed=100確かめること。
Device::begin()が2回出ている。LEDもMotorもbegin()を書き直していないのに、どちらでも呼べているLED.cppからdigitalWrite(pin, HIGH)と書けるのは、pinがprotectedだから。privateに変えるとコンパイルエラーになるsetSpeed(500)が100に丸められている。これはMotor固有の機能で、Deviceには置けないRobotもDeviceを継承すべきか、「Robot is a Device」と声に出して確かめる
演習:クラス構造を考える
Device ├── LED ├── Motor └── Servo
Deviceに共通して置けるものは何かを考える(ピン番号・初期化など)- それぞれ固有の機能は何かを書き出す
- 「is-a」で説明できるかを確認する