自作武器を作ろうず

<!--リトルカブにカブ90(HA02)のエンジン積んで,ペットボトルロケットでロケットランチャーを作り,ChromebookでETロボコン出て,Arduinoでウンコ妨害装置る沖縄の学生のブログです。院生になりました-->


【C++】「非staticメンバ関数内のstatic付きローカル変数」でどハマりした話。

スポンサードなんとか

結論

  非staticメンバ関数内で宣言したstatic付きローカル変数は,クラス全体で1つの変数として扱われます(=static付きのメンバ変数と同じ挙動)。オブジェクトを複数生成してもstatic付きローカル変数はクラス全体で共有されてしまうから気をつけましょう。


…という話を,モデルケースを交えて以下つらつらと説明していきます。




あらすじ

  ところで僕は百合キャッキャ本が好きです。また女騎士のお話も好きです。Twitterを眺めてるとしばしばその手の神絵師さんの落書きが流れてきて,その度に僕の「百合センサー」と「女騎士センサー」が反応します。
  という訳でここでは例として「『百合センサの反応した回数』と『女騎士センサの反応した回数』を別々にカウントするプログラム」を題材に説明します。


作ろうとしてるプログラム
  • クラス”mySensor” から2つのオブジェクト "yuriSensor", "womanKnightSensor"を生成。
  • メンバ関数 ”funcCalledCounter” が呼びだされた回数を,個別に記録する。
  「百合センサは○回 / 女騎士センサ は○○回反応しました」って感じで個別にカウントできれば成功です。


悪い例(今回僕がハマった例)

#include <iostream>
using namespace std;

class mySensor{
  public:
    void funcCalledCounter(char name[16]){
      static int funcCount; //←これ。このStatic。
      funcCount ++;
      cout << name <<  "は" << funcCount << "回反応しました。"<< endl;
  };
};


int main(void){
  static mySensor *yuriSensor;
  static mySensor *womanKnightSensor;

  yuriSensor        = new mySensor();
  womanKnightSensor = new mySensor();

  for(int i=0;i<2;i++){ //百合センサが2回反応する
    yuriSensor -> funcCalledCounter("百合センサ"); 
  }
    
  for(int i=0;i<3;i++){ //女騎士センサが3回反応する
    womanKnightSensor -> funcCalledCounter("女騎士センサ"); 
  }
}



実行結果

百合センサは1回反応しました。
百合センサは2回反応しました。
女騎士センサは3回反応しました。
女騎士センサは4回反応しました。
女騎士センサは5回反応しました。



  「関数を抜けても値を変数に保存させたいときは static をつける」というC言語のノリで,メンバ関数内でstatic 付きローカル変数を宣言しました(7行目)。しかし,static int funcCount は,mySensor 全体で1つの変数となっており,言い換えると百合センサと女騎士センサで別々の static int funcCount を持っているわけではありません。したがって,百合センサ がstatic int funcCount の値を更新した後に,女騎士センサから static int funcCount にアクセスすると変更後の値となってしまってます。

  言ってしまえば「static 付きメンバ変数と同じ挙動してるだけじゃん」なのですが,僕は「非staticなメンバ関数内で使ってる変数なんだから当然オブジェクトごとに変数持ってるでしょ〜〜」と思い込んでたためにどハマりして2日間潰す羽目になりました。原稿提出2週間前の貴重な48時間やぞ。返せよマジで*1



解決策:static funcCount の代わりにメンバ変数を用意してやる

#include <iostream>
using namespace std;

class mySensor{
  public:
    void funcCalledCounter(char name[16]){
      funcCount ++;
      cout << name <<  "は" << funcCount << "回反応しました。"<< endl;
  };
  
  private:
    int funcCount;  //カウント用にメンバ変数を追加する
};


int main(void){
  static mySensor *yuriSensor;
  static mySensor *womanKnightSensor;

  yuriSensor        = new mySensor();
  womanKnightSensor = new mySensor();

  for(int i=0;i<2;i++){ //百合センサが2回反応する
    yuriSensor -> funcCalledCounter("百合センサ"); 
  }
    
  for(int i=0;i<3;i++){ //女騎士センサが3回反応する
    womanKnightSensor -> funcCalledCounter("女騎士センサ"); 
  }
}


実行結果

百合センサは1回反応しました。
百合センサは2回反応しました。
女騎士センサは1回反応しました。
女騎士センサは2回反応しました。
女騎士センサは3回反応しました。

  static int funcCount を消し,メンバ変数として新たにfuncCount を定義してあげることにより(11,12行目),「百合用のfuncCount」「女騎士用のfuncCount」として振る舞ってくれるようになりました。
  こうして王国に再び平和が訪れました。めでたしめでたし。






【日記】
  研究やら学会やら今あることを全部投げ出して,女の子に変身して女の子にハグしてハグされて癒やされたい。

*1:念の為いっとくけど大学の研究で百合センサの研究してるわけではありません