LoginSignup
2
1

More than 3 years have passed since last update.

C++のポインタの中身を知りたい

Last updated at Posted at 2018-09-18

実行環境

wandbox
gcc 7.3.0
g++ prog.cc -Wall -Wextra -std=c++11 
https://wandbox.org/permlink/yj0ruS6pPwGgwAFY

知りたいもの

継承関係を持ついくつかの構造体が定義されているとする.

struct A{
    int x;
    A(int _x):x(_x){ }
    virtual ~A(){ }
};

struct B1: public A{
    int y;
    B1(int _x, int _y):A(_x), y(_y){ }
    ~B1(){ }
};

struct B2: public A{
    int y;
    B2(int _x, int _y):A(_x), y(_y){ }
    ~B2(){ }
};

~A()virtual が付いているから,プログラマはきっと
子クラスを親クラスのポインタに入れて利用するはず.

    unique_ptr<A> p(new B1(1, 2));

p の中に A が入っているのか, B1 が入っているのか,B2 が入っているのか知りたい.

解決策

typeid

typeid情報を使うと,実行時型情報を取り出すことが出来る.
*pの型情報とB1の型情報を取り出して比較すれば,先程の知りたい情報が得られる.

    unique_ptr<A> p(new B1(1, 2));
    if (typeid(*p) == typeid(A))
        cout << "A" << endl;
    if (typeid(*p) == typeid(B1))
        cout << "B1" << endl;
    if (typeid(*p) == typeid(B2))
        cout << "B2" << endl;

実行すると B1 と表示されるはず.

関数化

毎回 typeid(*p) == typeid(B1)と書くのも妙な気がしたので,テンプレートを使った関数にまとめた.

template<typename T, typename U>
inline bool typeis1(const U& a1) {
    return typeid(a1) == typeid(T);
}

使い方は次のようになる.

    unique_ptr<A> p(new B1(1, 2));
    if (typeis1<B1>(*p))
        cout << "B1" << endl;
    if (typeis1<B2>(*p))
        cout << "B2" << endl;

ケアレスミスの回避

特に自分はうっかりミスをしてしまいがちなので,次のようなコードを書いてしまう.

コンパイルエラーも起きないし,ただfalseが返るだけなので,ランタイムエラーにもならない.

    unique_ptr<A> p(new B1(1, 2));

    // ポインタを渡している
    if (typeis1<B1>(p))
        cout << "typeis1<B1>(p)" << endl;

    unique_ptr<string> px(new string());

    // 無関係な型の値を渡している
    if (typeis1<B1>(*px))
        cout << "typeis1<B1>(*px)" << endl;

継承関係が成立していないならば,コンパイルエラーを吐き出すようにしたい.
これはメタ関数を使って実装できる.

template<typename Derived, typename Base,
    typename enable_if<is_base_of<Base, Derived>::value>::type* = nullptr>
inline bool typeis2(Base& a1) {
    return typeid(a1) == typeid(Derived);
}

使い方は次のようになる.

    unique_ptr<A> p(new B1(1, 2));
    unique_ptr<string> px(new string());

    if (typeis2<B1>(*p))
        cout << "typeis2<B1>(*p)" << endl;
    // コメントアウトを外すとコンパイルエラー
    // if (typeis2<B1>(p))
    //     cout << "typeis2<B1>(p)" << endl;
    // if (typeis2<B1>(*px))
    //     cout << "typeis2<B1>(*px)" << endl;

おまけ

RTTI(実行時型情報)が利用できない場合(コンパイルフラグに-fno-rttiがあるなど)、
typeidがソースコードに含まれるとコンパイルエラーになります。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1