第11回 解答例

IMUとGPSをvirtualで扱う

演習の解答例。まず自分で書いてから読むこと。

解答

include/Sensor.hpp
#pragma once

class Sensor {
public:
    virtual ~Sensor() = default;

    virtual void update();
};
src/Sensor.cpp
#include <Arduino.h>

#include "Sensor.hpp"

void Sensor::update() {
    Serial.println("Sensor::update");
}
include/IMU.hpp
#pragma once

#include "Sensor.hpp"

class IMU : public Sensor {
public:
    void update() override;
};
src/IMU.cpp
#include <Arduino.h>

#include "IMU.hpp"

void IMU::update() {
    Serial.println("IMU::update");
}

確認する

#include <Arduino.h>

#include "GPS.hpp"
#include "IMU.hpp"
#include "Sensor.hpp"

IMU imu;
GPS gps;

void setup() {
    Serial.begin(115200);

    Sensor* sensor = &imu;
    sensor->update();       // IMU::update  ← virtual だから

    sensor = &gps;
    sensor->update();       // GPS::update
}

void loop() {
}

virtualを外すとどうなるか

Sensor::update()からvirtualを消して、同じコードを動かす。

Sensor::update()の宣言sensor->update()の出力
virtual void update();IMU::update(実際のオブジェクトで決まる)
void update();Sensor::update(変数の型で決まる)

virtualがないと、コンパイラは変数の型(Sensor*)だけを見て呼び先を決める。virtualを付けると、実行時に「実際に入っているオブジェクト」を見て決める。この違いは必ず自分で動かして確認すること。

overrideを必ず付ける

class IMU : public Sensor {
public:
    void updata() override;   // 綴りミス → コンパイルエラーになる
};

overrideがなければ、これはSensor::update()とは無関係な新しい関数として受け入れられ、エラーにならない。そしてsensor->update()はSensorの実装を呼び続ける。原因を見つけるのが非常に難しいバグになる。

→ 第11回の本文に戻る