27
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【C++】void*を使った構造体やクラスの判別方法

Posted at

voidポインタを使ったクラス・構造体の判別方法

タイトルの通り、void*を使ったりtemplateを使って汎用性のあるコード作れると役に立つなと思いませんか?
現在、アルゴリズムを作りつつ得たノウハウをメモとして残します。

1. そもそも、voidポインタって何?

【void*】とは、どんな型のポインタにも対応できる万能ポインタの事です。
【void*】が指している型は、charやint、short, long, (複合体である)structやclassかもしれない・・・
だからこそ、汎用性が高いのに使いにくいのが【void*】です。

2. void*を使ってクラスや構造体を切り分けるとはどういうこと?

void*とは、何でも表現できるのにそれを切り分けるようにするとはどういうこと? と思った方もいらっしゃると思います。
切り分けるために必要なキーワードは、『フラグ・ID』です。
構造体やクラスの頭に必ず、識別子に代わるIDがあれば判別できると思います。
例えば、

ssample.cpp
typedef struct stSample {
  char  m_ID; // ← このように、先頭に識別の役割をするIDを置く
  char* m_pData;
} Sample;

すると、メモリの配置としては以下のようなイメージになっていると思います。

定義した構造体がメモリ上でどのようにマップされるか.png

32bit CPUの場合は、ポインタで扱うアドレス自体が4Byteなので、ポインタの変数の部分が4Byteになってます。
※ これは、doubleでもvoidでも『ポインタ』であればみな同じです。 (64bitでは、8Byteになります。)
ポイントは、定義順に前から詰めてメモリ上にマップされるということを理解頂ければと思います。

3.実際に簡単なコードでイメージしてみましょう

こんな感じで書いてみました。 (コンソールのコードですみません。)
あまり意味のないコードですみません…

sample.cpp
enum structType {
  structType_1 = 1,
  structType_2
}

typedef struct stSample1 {
  char  m_ID;
  char* m_pData;
} Sample1;

typedef struct stSample2 {
  char    m_ID;
  double m_Data;
} Sample2;

void disp(void* pData);

void main() {
  Sample1 sample1;
  Sample2 sample2;

  sample1.m_ID = structType_1;
  sample1.m_pData = "てすと";
  sample2.m_ID = structType_2;
  sample2.m_Data = 0.125;

  disp(sample1);
  disp(sample2);
}

void disp(void* pData) {
  // ((char*)pData)[0] が 2.の画像にある【m_ID】を表しています。
  switch( ((char*)pData)[0] ) {
  case structType_1:
    Sample1* pTmp = (Sample1*)pData;
    printf("Data: %s\n", pTmp->m_pData);
    break;
  case structType_2:
    Sample2* pTmp = (Sample2*)pData;
    printf("Data: %f\n", pTmp->m_Data);
    break;
  }
}

4.セキュリティーを意識する

3.で書いたコードですと外部公開する場合に、IDを改ざんされて意図しない動作に陥る可能性があります。
その場合は、以下のようにアクセス制限を付与すると安全になります。 (ただし、C言語では無理です。)
賛否両論あるかとは思いますが、、、

sample.cpp
enum structType {
  structType_1 = 1,
  structType_2
}

typedef struct stSample1 {
private:
  char  m_ID;

public:
  stSample1() {
    m_ID = structType_1;
  }

public:
  char* m_pData;
} Sample1;

typedef struct stSample2 {
private:
  char    m_ID;

public:
  stSample1() {
    m_ID = structType_2;
  }

public:
  double m_Data;
} Sample2;

まとめ

void*を使った構造体やクラスの判別方法を書きました。
クラスや構造体の先頭に識別用の変数を用意してあげる事で、アドレスの頭からIDの変数の型のByte分だけ取得することで、
識別用の変数にあたる値を取れるので、利用できるよって話でした。

27
32
21

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
27
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?