0. 总论
开门见山,先把结论写在这里:
1)static_cast 在做基类指针和派生类指针之间的转换时,会根据编译时的静态偏移操作指针,但是没有运行期的类型安全检查,程序员需要自己确保类型的正确性,比如派生类指针确实指向了派生类对象。
2)dynamic_cast 依赖于虚表做运行期类型检查,适用于有虚函数的类型转换。
3)reinterpret_cast是最不安全的类型转换,完全暴力强制转换。
以下代码都是在X86_64 Linux中完成,g++编译。
1. 没有虚函数的单继承
1.1 static_cast
由于是单继承,派生类对象的内存起始部分就是基类信息,而static_cast也不做运行期的类型检查,因此派生类对象指针和基类指针可以自由转换。
转换前后指针都指向派生类对象起始地址。
1.2 dynamic_cast
dynamic_cast需要做运行时类型检查,向上转换(派生类到基类)没有问题,因为派生类的内存布局中起始部分就是基类信息,但是对于向下转换(基类到派生类)来说,在没有虚函数(虚表)的情况下,无法关联typeinfo信息,没法保证一个基类指针指向的是不是派生类。
1.3 reinterpret_cast
暴力转换,转换前后指针都指向派生类对象起始地址。
1.4 三种转换的示例代码:
#include <iostream>
class Base {
public:
int baseValue;
Base(int v) : baseValue(v) {}
void fool() {
std::cout << "Base class fool()" << std::endl;
}
};
class Derived : public Base {
public:
int derivedValue;
Derived(int b, int d) : Base(b), derivedValue(d) {}
void fool() {
std::cout << "Derived class fool()" << std::endl;
}
};
int main() {
Derived* dPtr = new Derived(20,30); // 创建 Derived 对象
std::cout << "sizeof(Base): " << sizeof(Base) << std::endl;
std::cout << "sizeof(Derived): " << sizeof(Derived) << std::endl;
std::cout << "Derived Ptr: " << dPtr << std::endl;
// 1. 使用 static_cast
std::cout << "### Using static_cast:" << std::endl;
Base* basePtr1 = static_cast<Base*>(dPtr); // Derived* 转换为 Base*
std::cout << "static_cast basePtr1: " << basePtr1 << std::endl;
basePtr1->fool(); // 调用 Base 的 fool()
std::cout << "basePtr1->baseValue is " << basePtr1->baseValue << std::endl;
// 从 Base* 转回 Derived*
Derived* derivedPtr1 = static_cast<Derived*>(basePtr1); // Base* 转换为 Derived*
std::cout << "static_cast derivedPtr1: " << derivedPtr1 << std::endl;
derivedPtr1->fool(); // 调用 Derived 的 fool()
std::cout << "derivedPtr1->baseValue is " << derivedPtr1->baseValue << std::endl;
std::cout << "derivedPtr1->derivedValue is " << derivedPtr1->derivedValue << std::endl;
// 2. 使用 dynamic_cast
std::cout << "\n### Using dynamic_cast:" << std::endl;
Base* basePtr2 = dynamic_cast<Base*>(dPtr); // Derived* 转换为 Base*
std::cout << "dynamic_cast basePtr2: " << basePtr2 << std::endl;
basePtr2->fool(); // 调用 Base 的 fool()
std::cout << "basePtr2->baseValue is " << basePtr2->baseValue << std::endl;
// 由于没有虚函数,dynamic_cast 无法正常使用,这行代码会导致编译错误
//Derived* derivedPtr2 = dynamic_cast<Derived*>(basePtr2);
// 3. 使用 reinterpret_cast
std::cout << "\n### Using reinterpret_cast:" << std::endl;
Base* basePtr3 = reinterpret_cast<Base*>(dPtr); // Derived* 转换为 Base*
std::cout << "reinterpret_cast basePtr3: " << basePtr3 << std::endl;
basePtr3->fool(); // 调用 Base 的 fool()
std::cout << "basePtr3->baseValue is " << basePtr3->baseValue << std::endl;
// 从 Base* 强制转换回 Derived*
Derived* derivedPtr3 = reinterpret_cast<Derived*>(basePtr3); // 将 Base* 强制转换为 Derived*
std::cout << "reinterpret_cast derivedPtr3: " << derivedPtr3 << std::endl;
derivedPtr3->fool(); // 调用 Derived 的 fool()
std::cout << "derivedPtr3->baseValue is " << derivedPtr3->baseValue << std::endl;
std::cout << "derivedPtr3->derivedValue is " << derivedPtr3->derivedValue << std::endl;
// 清理内存
delete dPtr;
return 0;
}
运行结果:
$ ./cast1
sizeof(Base): 4
sizeof(Derived): 8
Derived Ptr: 0x25a8eb0
### Using static_cast:
static_cast basePtr1: 0x25a8eb0
Base class fool()
basePtr1->baseValue is 20
static_cast derivedPtr1: 0x25a8eb0
Derived class fool()
derivedPtr1->baseValue is 20
derivedPtr1->derivedValue is 30
### Using dynamic_cast:
dynamic_cast basePtr2: 0x25a8eb0
Base class fool()
basePtr2->baseValue is 20
### Using reinterpret_cast:
reinterpret_cast basePtr3: 0x25a8eb0
Base class fool()
basePtr3->baseValue is 20
reinterpret_cast derivedPtr3: 0x25a8eb0
Derived class fool()
derivedPtr3->baseValue is 20
derivedPtr3->derivedValue is 30
1.5 内存布局图解
2. 有虚函数的单继承
2.1 示例代码
上面的代码稍作修改,将fool()变成虚函数,同时dynamic_cast向下转换可以生效:
#include <iostream>
class Base {
public:
int baseValue;
Base(int v) : baseValue(v) {}
virtual void fool() {
std::cout << "Base class fool()" << std::endl;
}
};
class Derived : public Base {
public:
int derivedValue;
Derived(int b, int d) : Base(b), derivedValue(d) {}
virtual void fool() override {
std::cout << "Derived class fool()" << std::endl;
}
};
int main() {
Derived* dPtr = new Derived(20,30); // 创建 Derived 对象
std::cout << "sizeof(Base): " << sizeof(Base) << std::endl;
std::cout << "sizeof(Derived): " << sizeof(Derived) << std::endl;
std::cout << "Derived Ptr: " << dPtr << std::endl;
// 1. 使用 static_cast
std::cout << "### Using static_cast:" << std::endl;
Base* basePtr1 = static_cast<Base*>(dPtr); // Derived* 转换为 Base*
std::cout << "static_cast basePtr1: " << basePtr1 << std::endl;
basePtr1->fool(); // 调用 Base 的 fool()
std::cout << "basePtr1->baseValue is " << basePtr1->baseValue << std::endl;
// 从 Base* 转回 Derived*
Derived* derivedPtr1 = static_cast<Derived*>(basePtr1); // Base* 转换为 Derived*
std::cout << "static_cast derivedPtr1: " << derivedPtr1 << std::endl;
derivedPtr1->fool(); // 调用 Derived 的 fool()
std::cout << "derivedPtr1->baseValue is " << derivedPtr1->baseValue << std::endl;
std::cout << "derivedPtr1->derivedValue is " << derivedPtr1->derivedValue << std::endl;
// 2. 使用 dynamic_cast
std::cout << "\n### Using dynamic_cast:" << std::endl;
Base* basePtr2 = dynamic_cast<Base*>(dPtr); // Derived* 转换为 Base*
std::cout << "dynamic_cast basePtr2: " << basePtr2 << std::endl;
basePtr2->fool(); // 调用 Base 的 fool()
std::cout << "basePtr2->baseValue is " << basePtr2->baseValue << std::endl;
// 从 Base* 转回 Derived*
Derived* derivedPtr2 = dynamic_cast<Derived*>(basePtr2);
std::cout << "dynamic_cast derivedPtr2: " << derivedPtr2 << std::endl;
derivedPtr2->fool(); // 调用 Derived 的 fool()
std::cout << "derivedPtr2->baseValue is " << derivedPtr2->baseValue << std::endl;
std::cout << "derivedPtr2->derivedValue is " << derivedPtr2->derivedValue << std::endl;
// 3. 使用 reinterpret_cast
std::cout << "\n### Using reinterpret_cast:" << std::endl;
Base* basePtr3 = reinterpret_cast<Base*>(dPtr); // Derived* 转换为 Base*
std::cout << "reinterpret_cast basePtr3: " << basePtr3 << std::endl;
basePtr3->fool(); // 调用 Base 的 fool()
std::cout << "basePtr3->baseValue is " << basePtr3->baseValue << std::endl;
// 从 Base* 强制转换回 Derived*
Derived* derivedPtr3 = reinterpret_cast<Derived*>(basePtr3); // 将 Base* 强制转换为 Derived*
std::cout << "reinterpret_cast derivedPtr3: " << derivedPtr3 << std::endl;
derivedPtr3->fool(); // 调用 Derived 的 fool()
std::cout << "derivedPtr3->baseValue is " << derivedPtr3->baseValue << std::endl;
std::cout << "derivedPtr3->derivedValue is " << derivedPtr3->derivedValue << std::endl;
// 清理内存
delete dPtr;
return 0;
}
运行结果;
$ ./cast2
sizeof(Base): 16
sizeof(Derived): 16
Derived Ptr: 0x222ceb0
### Using static_cast:
static_cast basePtr1: 0x222ceb0
Derived class fool()
basePtr1->baseValue is 20
static_cast derivedPtr1: 0x222ceb0
Derived class fool()
derivedPtr1->baseValue is 20
derivedPtr1->derivedValue is 30
### Using dynamic_cast:
dynamic_cast basePtr2: 0x222ceb0
Derived class fool()
basePtr2->baseValue is 20
dynamic_cast derivedPtr2: 0x222ceb0
Derived class fool()
derivedPtr2->baseValue is 20
derivedPtr2->derivedValue is 30
### Using reinterpret_cast:
reinterpret_cast basePtr3: 0x222ceb0
Derived class fool()
basePtr3->baseValue is 20
reinterpret_cast derivedPtr3: 0x222ceb0
Derived class fool()
derivedPtr3->baseValue is 20
derivedPtr3->derivedValue is 30
2.2 内存布局图解
3. 没有虚函数的多继承
类图关系如下:
3.1 static_cast
可见,将Derived Ptr static_cast到Base2 Ptr以后,这个指针指向Derived instance内存布局当中Base2的部分起始地址。如果继续再做反方向的static_cast, 从 Base2 Ptr还可以回到Derived Ptr
3.2 dynamic_cast
没有虚函数的情况下,只允许派生类指针到基类指针的转换,基类指针不允许转为派生类指针。
3.3 reinterpret_cast
暴力转换,强制类型转换
此时,如果通过Base2 *b2ptr去访问Base2部分的内存内容会出问题,它并不像static_cast或者dynamic_cast那样做了指针偏移。
4. 有虚函数的多继承
Derived实例化以后的内存布局示意图:
4.1 static_cast
4.2 dynamic_cast
4.3 reinterpret_cast
暴力转换,强制转换
此时,如果通过Base2 *b2ptr去访问Base2部分的内存内容会出问题,比如:
操作 b2ptr->baseValue2其实操作的是baseValue1
操作 b2ptr->foo2() 实际操作的是foo1()