C++ Casting

C++ 中有四種 Cast:const_caststatic_castdynamic_castreinterpret_cast,這些 cast function 不是 marco 也不屬於 STL Libray,是 C++ 的保留字(Keyword)。

const_cast

const_cast 的用途是移除 const 的屬性。
強烈建議不要用,除非有必須。
用法:const_cast<T>(ptr),其中 ptr 必為指標或 refrence
範例如下:

  1. 移除 a 的 const 屬性,並且修改 a 的值
const int a = 10;
const int *ptr = &a;
int *cc = const_cast<int *>(ptr);
*cc = 99;

std::cout << "*a = " << *a << "\n";
std::cout << "*ptr = " << *ptr << "\n";
std::cout << "*cc = " << *cc << "\n";

// output:
// *a = 99
// *ptr = 99
// *cc = 99
  1. 將 const int* 的參數型別改成 int*
void foo(int *var) { ... }

int main() {
    const int a = 19;
    const int *ptr = &a;
    
    foo(ptr);
    // Compile Error
    
    foo(const_cast<int*>(ptr));
    // Valid
}

static_cast

執行於 compile-time,功能與 C-Style cast 相似,但更安全。
範例:

  1. 特別標注 c-style cast (implicit cast),增加可讀性及提醒 reader
float f = 3.5;

int n1 = (int)f;
int n2 = static_cast<int>(f);

// n1 = n2 = 3
  1. 避免不合理的型別轉換
char c = 'a';
int *ptr1 = (int *)&c;
// sizeof(char) = 1 byte,
// sizeof(int*) = 4 byte

// This is valid but dangerous since it'll access undefined memory block.
*ptr = 1;
// c = ???
// *ptr = 1


int *ptr2 = static_cast<int*>(c);
// Compile Error
class Base {};
class Child : private Base {};

int main() {
    Child child;
    Base *ptr1 = (Base*)&child;    // Allowed at compile-time
    Base *ptr2 = static_cast<Base*>(child)    // Compile Error
    // Can't access private base class
}
  1. 任何用到 void* 的時機
int n = 10;
void *v = static_cast<void*>(&n);
int *ip = static_cast<int*>(v);

dynamic_cast

執行於 run-time,用於 polymorphism,up cast (Derived -> Base) 或 down cast (Base -> Derived)。
若已知型別轉換不會出錯,建議使用 static_cast,執行時間較快。
範例:

  1. 檢查繼承關係是否正確
class Base {
    virtual void foo(){};
};
class Derived1 : public Base {};
class Derived2 : public Base {};

int main() {
    Derived1 d1;
    
    // Valid
    Base *pBase = dynamic_cast<Base*>(d1);
    Derived1 *pd1 = dynamic_cast<Derived1*>(pBase);

    Derived2 *pd2 = dynamic_cast<Derived2*>(d1);
    // pd2 is a nullptr
}

reinterpret_cast

是個很危險的存在,有不可兼容的危險(importable)。
用途是強制轉換型別,不論資料大小是否相同。

範例:

struct S {
    int x,    // 4 byte
    int y,    // 4 byte
    char c,   // { 4 byte
    bool b    // }
};

int main() {
    S s = { 1, 2, 'a', true };
    int *pInt = reinterpret_cast<int*>(&s);
    std::cout << *pInt << "\n";    // 1
    pInt++;
    std::cout << *pInt << "\n";    // 2
    pInt++;
    char *pChar = reinterpret_cast<char*>(pInt);
    std::cout << *pChar << "\n";   // a
    pChar++;
    bool *pBool = reinterpret_cast<bool*>(pChar);
    std::cout << *pBool << "\n";   // 1
}