C++ 指標轉型後位置可能會改變

今天踩到一個和 C++ 指標轉型相關的 bug, 幸好以前看《深度探索 C++ 物件模型》時有個模糊的印象, 知道物件指標轉型後, 位置可能會變, 發現這問題後很快就想通了。

先寫個小程式實驗一下:

#include <iostream>

struct A { int x; };
struct B { int y; };
struct C : public A, B { int z; };

int main() {
  C *c = new C();
  A *a = c;
  B *b = c;
  void* v = c;
  std::cout << a << " " << b << " " << c << " " << v << std::endl;
  return 0;
}

範例輸出:

0xbd0010 0xbd0014 0xbd0010 0xbd0010

由此可得知 a、c、v 位置一樣, 但 b 不同

若用指標相等做邏輯判斷, 要留意轉型帶來的影響, 特別是中間有轉型成 void* 的時候, 可能傳了同樣的物件, 卻因型別不同造成誤判。

以下是一個示意的例子:

class Foo final
{
public:
  void Save(const Data& data, void* source);
  ...

private:
  ...
  FooCallback *m_callback;
}

void Foo::Save(const Data& data, void* source)
{
  ...
  if (m_callback && source != m_callback) {
    m_callback->OnSave(data);
  }
}

有不同的來源會呼叫 Foo::Save, 此時 Foo 會通知事先註冊的 callback, 但要避開 callback object 主動呼叫 Foo::Save 的情況。

乍看之下有檢查 source != m_callback 即可安心。但若 callback object (即有繼承 FooCallback 的類別的物件) 呼叫 Foo::Save 時這麼寫:

Foo *foo = new Foo();
...
foo->Save(data, this);

有可能因指標型別不同, 而得到不同的位置, 導致 source != m_callback 為真。

反之, 這樣的寫法才能安全過關:

foo->Save(data, (FooCallback*)this);

C++ 的世界真是處處驚奇啊!

留言

  1. struct C 同時有 A, B 兩個部份, 而它的 B 部份又剛好放在 A 後面, 如此而已 ...


    ref.《深度探索C++ 物件模型》

    回覆刪除
  2. 《深度探索C++ 物件模型》真是好書, 就是有看過它, 遇到的時候才沒有大叫「搞什麼鬼~~~」XD

    回覆刪除

張貼留言

這個網誌中的熱門文章

(C/C++ ) 如何在 Linux 上使用自行編譯的第三方函式庫

熟悉系統工具好處多多

virtualbox 使用 USB 裝置