#はじめに
この記事の内容は、ポリモーフィズムやC++の継承周りへの知識が少ない筆者が我流で編み出しただけのものなので、これが必要となる設計が正しいのかは保証しません。
とりあえず動けばいいという方だけ参考にしていただければ。
また、この記事の内容は、恐らく分かる人からしたら記事にするまでもない当然のことだとは思うのですが、この間詰まってしまったので、自分のための備忘録も兼ねて公開しています。
#やりたいこと
struct Base{
//派生クラスが持つ static メンバを定義する術はない……
};
struct Derived : public Base{
static Hoge static_var;
};
Base* p_base=new Derived();
p_base->static_var;//Baseのメンバではないのでエラー
C++でポリモーフィズムをしようとするとき、たまに上のようなことをしたくなることがあります。
上の例ぐらいならダウンキャストで何とかなりますが、例えば複数種類の派生クラスのインスタンスをvectorで管理しようとすると、ダウンキャスト先を知ることも出来なくなります。(仮に知れたとしても、派生クラスを増やす度にダウンキャスト処理を書き足すのはエレガントじゃないですよね)
こういうとき、(残念ながらstatic変数の定義を強制することは出来ませんが)少なくともアクセスを可能にする方法が下にあるものです。
ちなみに私は、これを使って関数オブジェクトをstd::unordered_mapで管理しようとしていました。(「それなら仮想関数で出来るだろ」という意見は受け付けません)
#やりかた
非staticメンバであれば基底クラスが持っておくことができるので、アクセスしたいstaticメンバへのポインタを基底クラスに持たせるだけです。
あとは、基底クラスで定義したアクセス用ポインタに、派生クラスのコンストラクタでstaticメンバのアドレスを代入するだけです。
なお、インスタンス解放時に誤ってstaticメンバが解放されると困るので、それを防ぐためにshared_ptrとweak_ptrによるリソース管理を行っています。
##コード例
class Shared; //static変数として保持するクラス
class Base{
public:
std::weak_ptr<Shared> ptr_to_static;//コレを経由して派生クラスのstaticメンバにアクセス
//これがshared_ptrだと、Sharedがshared_ptr<Base>を持つとき循環参照が生じる
};
class Derived : public Base{
static std::shared_ptr<Shared> really_static;//実際にアクセスしたいメンバ
public:
Derived(){
ptr_to_static=really_static;//ここで登録
}
};
std::shared_ptr<Shared> Derived1::really_static=std::make_shared<Shared>();
挙動のチェックには下のテストコードを使いました。アクセス方法の実例を見たい場合はご覧ください。
https://wandbox.org/permlink/Nk7hcJqsbKFmfgnj
【追記】
@myoukaku さんからコメントを頂いた通り、仮想関数を使ったアプローチもあるようです。shared_ptrなど使わなくても実現できますし、定義忘れでコンパイルエラーを吐いてくれるなど、コメントの方法の方がbetterだと思います。(詳細はコメントを見てください)
#おわりに
「はじめに」でも書きましたが、C++のポリモーフィズムは不慣れなので、もしかするとアンチパターンをやっていたりするかもしれません。何かあればコメントでアドバイスを下さると嬉しいです。