アフィリエイト広告
アフィリエイト広告

Arduino で C++クラスを学ぶ (4) – インライン関数

前回は、コンストラクタを定義して、メンバ変数の初期化などを行なうことを学びました。

さて、できあがったスケッチは、以前みたことがあるような気が。そう、マルチタスクのレッスンで、初めて C++ クラスに出会ったときの、あのチンプンカンプンだった L チカ Flash のスケッチです。

スケッチの違い

2つのスケッチを比べてみましょう。
左は、マルチメディアのレッスンで使ったスケッチ。右は、今回作ったスケッチです。

以前のレッスンのスケッチ
  1. class Flasher
  2. {
  3.     // class Member Variables
  4.     // These are initialized at startup
  5.     int ledPin;
  6.     long OnTime;
  7.     long OffTime;
  8.     // These maintain the current state
  9.     int ledState;
  10.     unsigned long previousMillis;
  11.   // Constructor - creates a Flasher
  12.   // and initializes the member variables and state
  13.   public:
  14.   Flasher(int pin, long on, long off)
  15.   {
  16.     ledPin = pin;
  17.     pinMode(ledPin, OUTPUT);
  18.     OnTime = on;
  19.     OffTime = off;
  20.     ledState = LOW;
  21.     previousMillis = 0;
  22.   }
  23.   void Update()
  24.   {
  25.     // check to see if it's time to change the state of the LED
  26.     unsigned long currentMillis = millis();
  27.     if ((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
  28.     {
  29.       ledState = LOW;
  30.       previousMillis = currentMillis;
  31.       digitalWrite(ledPin, ledState);
  32.     }
  33.     else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
  34.     {
  35.       ledState = HIGH;
  36.       previousMillis = currentMillis;
  37.       digitalWrite(ledPin, ledState);
  38.     }
  39.   }
  40. };
  41. Flasher led1(12, 100, 400);
  42. Flasher led2(13, 350, 350);
  43. void setup()
  44. {
  45. }
  46. void loop()
  47. {
  48.   led1.Update();
  49.   led2.Update();
  50. }
今回作成したスケッチ
  1. class Flasher {
  2.   private:
  3.     byte ledPin;
  4.     int onTime;
  5.     int offTime;
  6.     byte ledStat;
  7.     unsigned long prevMillis;
  8.   public:
  9.     Flasher(byte pin, int on, int off);
  10.     void update();
  11. };
  12. Flasher::Flasher(byte pin, int on, int off) {
  13.   ledPin = pin;
  14.   pinMode(ledPin, OUTPUT);
  15.   onTime = on;
  16.   offTime = off;
  17.   ledStat = LOW;
  18.   prevMillis = 0;
  19. }
  20. void Flasher::update() {
  21.   unsigned long currMillis = millis();
  22.   if((HIGH == ledStat) && (onTime < currMillis - prevMillis)) {
  23.     ledStat = LOW;
  24.     prevMillis = currMillis;
  25.     digitalWrite(ledPin, ledStat);
  26.   }
  27.   else if((LOW == ledStat) && (offTime < currMillis - prevMillis)) {
  28.     ledStat = HIGH;
  29.     prevMillis = currMillis;
  30.     digitalWrite(ledPin, ledStat);
  31.   }
  32. }
  33. Flasher led1(12, 100, 400);
  34. Flasher led2(13, 350, 350);
  35. void setup() {
  36. }
  37. void loop() {
  38.   led1.update();
  39.   led2.update();
  40. }

コメントが書かれているとか、変数名が違うとか、型が違うとか、こまごました違いはありますが、ほぼ同じですよね。大きな違いといえるのは、メンバ関数の定義されている場所。
左のスケッチは、メンバ関数の定義が、クラスの宣言 class Flasher { }; の内に書かれています。今回作った右のスケッチでは、クラスの宣言の外です。

L チカ Flash の動作は、どちらも同じです。結果が違わないなら、どっちでもいい、ってことでしょ?

インライン関数

関数を使ったスケッチは、関数を使っていないスケッチに比べると、実行する時に関数を呼び出したり、引数や戻り値を受け渡したりするために、余分な時間がかかります。
そんなん気になるほどのスケッチは、いまの俺は書くこともないのですけど。しかしまぁ、ちょっとした処理をやってる関数を、何度も何度も呼び出していると、それなりに時間もかさむ。マルチタスクな処理をやっていると、無視できないこともでてくるかもしれません。

そんなときに役立つのが、インライン関数、です。

難しい説明は、詳しくないのでしません m(_ _;)m けど、ようするに、関数にかかる余計な時間が短縮できる場合がある、ってこと。

インライン関数にするには、関数の先頭に inline を付けます。

  • inline void Flasher::update() {
  •   // 処理....
  • }

が、クラスのメンバ関数の場合、クラスの宣言のなかで定義すれば、自動的にインライン関数になるそうです。

  • class Flasher {
  •   public:
  •     void update() {
  •       // 処理....
  •     }
  • };

マルチタスクのレッスンで使ったスケッチは、インライン関数です。今回俺が作ってきたスケッチは、普通の関数。そんな違いがあります。

レッスンスケッチが、インライン関数を意識していたかどうかは、わかりません。loop() のなかで繰返し実行するためにインライン関数とした、のかもしれませんし、クラス宣言のなかにすべてをまとめるほうが初心者にわかりやすいから、かもしれません。

どちらにしても。俺は当面はスケッチをファイル分割するつもりもないし、関数もクラスの宣言のなかで定義してしまう形のほうが、わかりやすいんじゃないかなと、思っています。

スケッチ – インライン関数

ということで、メンバ関数をクラスの宣言のなかに移動し、インライン関数としたスケッチです。

  1. class Flasher {
  2.   private:
  3.     byte ledPin;
  4.     int onTime;
  5.     int offTime;
  6.     byte ledStat;
  7.     unsigned long prevMillis;
  8.   public:
  9.     Flasher(byte pin, int on, int off) {
  10.       ledPin = pin;
  11.       pinMode(ledPin, OUTPUT);
  12.       onTime = on;
  13.       offTime = off;
  14.       ledStat = LOW;
  15.       prevMillis = 0;
  16.     }
  17.     void update() {
  18.       unsigned long currMillis = millis();
  19.       if((HIGH == ledStat) && (onTime < currMillis - prevMillis)) {
  20.         ledStat = LOW;
  21.         prevMillis = currMillis;
  22.         digitalWrite(ledPin, ledStat);
  23.       }
  24.       else if((LOW == ledStat) && (offTime < currMillis - prevMillis)) {
  25.         ledStat = HIGH;
  26.         prevMillis = currMillis;
  27.         digitalWrite(ledPin, ledStat);
  28.       }
  29.     }
  30. };
  31. Flasher led1(12, 100, 400);
  32. Flasher led2(13, 350, 350);
  33. void setup() {
  34. }
  35. void loop() {
  36.   led1.update();
  37.   led2.update();
  38. }

メンバ関数をクラスの宣言のなかに定義した場合、スコープ解決の Flasher:: をつける必要はありません。が、ついていても問題はありません。また、inline がついていても、問題ないです。

まとめ

先頭に inline がついた関数は、インライン関数です。
インライン関数は、コンパイルした時に呼び出される位置に埋め込まれるので、処理速度が向上する場合があります。でも、埋め込まれるかどうか、向上するかどうかは、コンパイラ任せ、運任せ。

クラスの宣言のなかで定義したメンバ関数は、インライン関数になります。loop() で何回も繰返し実行するスケッチでは、インライン関数のほうが有利じゃね?知らんけど (;´Д`)

さて、メンバ関数も関数の一種なので、オーバーロードとかデフォルト引数とかポインタを渡すとか、できます。メンバ変数も、定数とか静的変数とか、あります。
いろいろあるけど、クラスを使う方法はこんな感じかな。初めてスケッチをみたときはチンプンカンプンだったけど、わかってくると、使えるんじゃないかなぁ。これから、できるだけ使ってみようかなと。

タイトルとURLをコピーしました