C++ 子类与父类转型:static_cast、dynamic_cast
·
文章目录
一、强制类型转换
在C++中,类型转换涉及继承、多态、虚函数等概念。C语言可以对内置类型进行强制转换,但不够安全。C++标准提供了类型层次转换的两个关键字:static_cast 和 dynamic_cast。
二、static_cast 关键字(编译时类型检查)
用法:static_cast <type-id> (expression)
该运算符把 expression 转换为 type-id 类型,但没有运行时类型检查来保证转换的安全性。
2.1 以下几种用法
- 基本数据类型之间的转换:如把 int 转换为 char,把 int 转换成 enum
- 把空指针转换成目标类型的空指针
- 把任何类型的表达式类型转换成 void 类型
- 用于类层次结构中父类和子类之间指针和引用的转换
#include <iostream>
using namespace std;
// 基本数据类型转换
void basic_conversion() {
int i = 1000;
char c = static_cast<char>(i); // 可能丢失精度
cout << "char value: " << c << endl;
void* void_ptr = static_cast<void*>(&i); // 转换为void指针
int* int_ptr = static_cast<int*>(void_ptr); // 转回int指针
cout << "int value: " << *int_ptr << endl;
}
2.2 上行转换与下行转换
对于类层次转换,存在两种形式:
- 上行转换(子类转父类):安全的
- 下行转换(父类转子类):不安全的
class Base {
public:
virtual void fun() {}
};
class Derived : public Base {};
void conversion_example() {
// 上行转换:安全
Derived* d = new Derived();
Base* b1 = static_cast<Base*>(d); // 安全
// 下行转换:不安全(编译时检查,运行时可能出错)
Base* b2 = new Base();
Derived* d2 = static_cast<Derived*>(b2); // 不安全!
delete d;
delete b2;
}
三、dynamic_cast 关键字(运行时类型检查)
用法:同 static_cast
dynamic_cast 主要用于类层次结构中父类和子类之间指针和引用的转换,具有运行时类型检查功能。
关键要求:
- 必须有虚函数(RTTI支持)
- 必须本身就是目标类型,只是指针指向不同
class Base {
public:
virtual ~Base() {} // 必须有虚函数
};
class Derived : public Base {};
void dynamic_cast_example() {
Base* b1 = new Derived();
// 安全的向下转换
Derived* d1 = dynamic_cast<Derived*>(b1);
if (d1 != nullptr) {
cout << "转换成功" << endl;
}
Base* b2 = new Base();
// 不安全的向下转换
Derived* d2 = dynamic_cast<Derived*>(b2);
if (d2 == nullptr) {
cout << "转换失败,返回nullptr" << endl;
}
delete b1;
delete b2;
}
四、使用示例
4.1 直接强转
#include <iostream>
#include <string>
using namespace std;
// 父类
class SingleChip {
public:
string type;
// 虚函数
virtual void CloseLED() {
cout << "SingleChip Close LED" << endl;
}
void OpenLED() {
cout << "SingleChip Open LED" << endl;
}
};
// 子类
class STM32 : public SingleChip {
public:
void OpenLED() {
cout << type << " Open LED" << endl;
}
void CloseLED() {
cout << type << " Close LED" << endl;
}
};
int main() {
cout << "---没有转型前---" << endl;
SingleChip sc = SingleChip();
sc.type = "单片机";
sc.CloseLED(); // SingleChip Close LED
sc.OpenLED(); // SingleChip Open LED
return 0;
}
4.2 dynamic_cast 关键字强转
#include <iostream>
#include <string>
using namespace std;
class SingleChip {
public:
string type;
virtual void CloseLED() {
cout << "SingleChip Close LED" << endl;
}
void OpenLED() {
cout << "SingleChip Open LED" << endl;
}
virtual ~SingleChip() {} // 虚析构函数(满足RTTI要求)
};
class STM32 : public SingleChip {
public:
string info;
STM32(string info) {
this->info = info;
}
void OpenLED() {
cout << type << " Open LED info = " << info << endl;
}
void CloseLED() {
cout << type << " Close LED" << endl;
}
};
int main() {
cout << "---没有转型前---" << endl;
SingleChip* sc = new STM32("2021-10-04");
sc->type = "单片机";
sc->CloseLED(); // SingleChip Close LED
sc->OpenLED(); // SingleChip Open LED
cout << "---向下转型:父类转子类---" << endl;
STM32* stm32 = dynamic_cast<STM32*>(sc);
if (stm32 == nullptr) {
cout << "stm32 为空" << endl;
} else {
stm32->OpenLED(); // 单片机 Open LED info = 2021-10-04
stm32->CloseLED(); // 单片机 Close LED
}
delete sc;
return 0;
}
4.3 static_cast 关键字强转
#include <iostream>
#include <string>
using namespace std;
class SingleChip {
public:
string type;
virtual void CloseLED() {
cout << "SingleChip Close LED" << endl;
}
void OpenLED() {
cout << "SingleChip Open LED" << endl;
}
virtual ~SingleChip() {}
};
class STM32 : public SingleChip {
public:
void OpenLED() {
cout << type << " Open LED" << endl;
}
void CloseLED() {
cout << type << " Close LED" << endl;
}
};
int main() {
cout << "---没有转型前---" << endl;
SingleChip sc = STM32();
sc.type = "单片机";
sc.CloseLED(); // SingleChip Close LED
sc.OpenLED(); // SingleChip Open LED
cout << "---向下转型:父类转子类---" << endl;
// 注意:这里实际上发生了对象切片,转换可能不安全
STM32* sp2 = static_cast<STM32*>(&sc);
sp2->OpenLED(); // 单片机 Open LED
sp2->CloseLED(); // SingleChip Close LED(虚函数调用父类版本)
return 0;
}
4.4 子类具有独有字段
#include <iostream>
#include <string>
using namespace std;
class SingleChip {
public:
string type;
virtual void CloseLED() {
cout << "SingleChip Close LED" << endl;
}
void OpenLED() {
cout << "SingleChip Open LED" << endl;
}
virtual ~SingleChip() {}
};
class STM32 : public SingleChip {
public:
string info;
STM32(string info) {
this->info = info;
}
void OpenLED() {
cout << type << " Open LED info = " << info << endl;
}
void CloseLED() {
cout << type << " Close LED" << endl;
}
};
int main() {
SingleChip* sc = new STM32("2021-10-04");
sc->type = "单片机";
// 三种转换方式
STM32* sp1 = (STM32*)sc; // C风格强制转换
STM32* sp2 = static_cast<STM32*>(sc); // static_cast
STM32* sp3 = dynamic_cast<STM32*>(sc); // dynamic_cast
sp1->OpenLED(); // 单片机 Open LED info = 2021-10-04
sp2->OpenLED(); // 单片机 Open LED info = 2021-10-04
sp3->OpenLED(); // 单片机 Open LED info = 2021-10-04
delete sc;
return 0;
}
4.5 子类地址转父类指针
#include <iostream>
#include <string>
using namespace std;
class SingleChip {
public:
string type;
virtual void CloseLED() {
cout << "SingleChip Close LED" << endl;
}
void OpenLED() {
cout << "SingleChip Open LED" << endl;
}
virtual ~SingleChip() {}
};
class STM32 : public SingleChip {
public:
string info;
STM32(string info) {
this->info = info;
}
void OpenLED() {
cout << type << " Open LED info = " << info << endl;
}
};
int main() {
// 子类
STM32 stm32 = STM32("2021-10-04");
stm32.type = "STM32C8T6F103";
// 子类地址转父类指针
SingleChip* scp1 = (SingleChip*)&stm32;
cout << scp1->type << endl; // STM32C8T6F103
scp1->OpenLED(); // SingleChip Open LED
((STM32*)scp1)->OpenLED(); // STM32C8T6F103 Open LED info = 2021-10-04
return 0;
}
4.6 总测试
#include <iostream>
#include <string>
using namespace std;
class SingleChip {
public:
string type;
virtual void CloseLED() {
cout << "SingleChip Close LED" << endl;
}
void OpenLED() {
cout << "SingleChip Open LED" << endl;
}
virtual ~SingleChip() {}
};
class STM32 : public SingleChip {
public:
string info;
STM32(string info) {
this->info = info;
}
void OpenLED() {
cout << type << " Open LED info = " << info << endl;
}
void CloseLED() {
cout << "STM32 Close LED" << endl;
}
};
int main() {
cout << "没有转型前......" << endl;
SingleChip sc = SingleChip();
sc.type = "单片机";
sc.CloseLED(); // SingleChip Close LED
sc.OpenLED(); // SingleChip Open LED
cout << "向下转型:父类转子类......" << endl;
STM32* sp1 = (STM32*)≻
STM32* sp2 = static_cast<STM32*>(&sc);
STM32* sp3 = dynamic_cast<STM32*>(&sc);
sp1->CloseLED(); // STM32 Close LED
sp2->CloseLED(); // STM32 Close LED
if (sp3 == nullptr) {
cout << "sp3 为空指针" << endl;
} else {
sp3->CloseLED();
}
cout << "向上转型:子类转父类......" << endl;
STM32 stm32 = STM32("test");
SingleChip* scp0 = &stm32; // 兼容赋值
SingleChip* scp1 = (SingleChip*)&stm32; // C风格转换
SingleChip* scp2 = dynamic_cast<SingleChip*>(&stm32); // dynamic_cast
SingleChip* scp3 = static_cast<SingleChip*>(&stm32); // static_cast
// 调用的仍是子类的 CloseLED(),因为父类的虚函数被子类覆盖
scp0->CloseLED(); // STM32 Close LED
scp1->CloseLED(); // STM32 Close LED
scp2->CloseLED(); // STM32 Close LED
scp3->CloseLED(); // STM32 Close LED
// 非虚函数调用父类版本
scp0->OpenLED(); // SingleChip Open LED
scp1->OpenLED(); // SingleChip Open LED
scp2->OpenLED(); // SingleChip Open LED
scp3->OpenLED(); // SingleChip Open LED
return 0;
}
4.7 总结
- 只有子类可转为父类,父类不可转为子类,因为子类包含父类所有的元素
- 父类对象可接受任何子类对象——此时发生自动转型——转为父类类型——所以能够再转回来
- 子类向父类类型转换,直接强制转换就可以了,但实际仍然指向子类对象
- 子类指针强制转换为父类指针对象时,并没有实际丢失它原有内存空间(比父类多的那些部分)只是暂时不可访问。所以能再转回来
- 父类转为子类,转置后,只能操作子类从父类继承那一部分,再转回来 dynamic_cast 在将父类 cast 到子类时,父类必须要有虚函数,多态性
五、父类转子类
5.1 父类转子类
首先父类转子类是会直接报错的:
// 以下都会报错
STM32 s1 = SingleChip(); // 报错
SingleChip s2 = SingleChip(); // 报错
STM32 s3 = (STM32)s2; // 报错
父类转子类是一种很不正确的行为,编辑器会报错,所以只存在【父类指针】转【子类指针】的情况。
#include <iostream>
#include <string>
using namespace std;
class SingleChip {
public:
string type;
virtual void CloseLED() {
cout << "SingleChip Close LED" << endl;
}
};
class STM32 : public SingleChip {
public:
string info;
void OpenLED() {
cout << type << " Open LED info = " << info << endl;
}
};
int main() {
SingleChip sc = SingleChip();
sc.type = "单片机";
// 父类转子类
STM32* sp1 = static_cast<STM32*>(&sc);
cout << sp1->type << endl; // 单片机
sp1->CloseLED(); // SingleChip Close LED
// 以下会报错,因为父类没有子类的信息
// sp1->OpenLED(); // 报错:缺少info数据
// cout << sp1->info << endl; // 报错:父类转子类,但没有子类的东西
return 0;
}
5.2 本身就是子类
尽量不要父类转子类,很不安全。只有当它本身就是子类,只是被父类指针引用的时候才能安全使用父类转子类。
#include <iostream>
#include <string>
using namespace std;
class SingleChip {
public:
string type;
// 为了使用dynamic_cast()
virtual void CloseLED() {
cout << " Close LED" << endl;
}
};
class STM32 : public SingleChip {
public:
string info;
STM32(string info) {
this->info = info;
}
void OpenLED() {
cout << type << " Open LED info = " << info << endl;
}
};
int main() {
// 注意:这里创建的是子类对象,只是用父类指针指向它
SingleChip* sc = new STM32("2021-10-04");
sc->type = "STM32C8T6F103";
((STM32*)sc)->info = "2022-11-04";
// 第1种强转
((STM32*)sc)->OpenLED(); // STM32C8T6F103 Open LED info = 2022-11-04
// 第2种强转
static_cast<STM32*>(sc)->OpenLED(); // STM32C8T6F103 Open LED info = 2022-11-04
// 第3种强转
dynamic_cast<STM32*>(sc)->OpenLED(); // STM32C8T6F103 Open LED info = 2022-11-04
delete sc;
return 0;
}
关键理解:SingleChip* sc = new STM32("2021-10-04"); 压根就不是父类,仍然初始化的是子类,只是用一个父类指针去指向它。所以后面的强转只是指针类型的转换,只是换个指针去指向这个内存空间而已。
错误写法示例:
int main() {
// 只是把子类对象的值赋给这个父类(发生对象切片)
SingleChip sc = STM32("2021-10-04");
sc.type = "STM32C8T6F103";
// 以下全部报错或行为未定义,因为info类型丢失
// ((STM32*)&sc)->OpenLED(); // 报错
// static_cast<STM32*>(&sc)->OpenLED(); // 报错
// dynamic_cast<STM32*>(&sc)->OpenLED(); // 返回nullptr
return 0;
}
总结:
- C++中层次类型转换中无非两种:上行转换和下行转换
- 对于上行转换,static_cast 和 dynamic_cast 效果一样,都安全
- 对于下行转换:你必须确定要转换的数据确实是目标类型的数据,即需要注意要转换的父类类型指针是否真的指向子类对象
- 如果是,static_cast 和 dynamic_cast 都能成功
- 如果不是,static_cast 能返回,但是不安全,可能会出现访问越界错误,而 dynamic_cast 在运行时类型检查过程中,判定该过程不能转换,返回 NULL
更多推荐



所有评论(0)