在之前写过一篇 C 类型转换的博客 【C 语言】类型转换 ( 转换操作符 | const_cast | static_cast | dynamic_cast | reinterpret_cast | 字符串转换 ) , 简单介绍了 C 类型转换 ;
在 博客 【C 】类型转换 ① ( C 中的类型转换 | C 类型转换操作符 | const_cast | static_cast | dynamic_cast | reinterpret_cast ) 将 C 语言 和 C 中的类型转换进行了对比 ;
在 博客 【C 】类型转换 ② ( C 静态类型转换 static_cast | C 语言隐式转换弊端 | 代码示例 ) 中 , 主要分析了 静态类型转换 static_cast , 可以解决 C 语言隐式转换的弊端 ;
在博客 【C 】类型转换 ③ ( 重新解释类型转换 reinterpret_cast | 指针类型数据转换 ) 分析了 指针数据类型的转换 , 在 C 语言环境下 , 可以使用显示强制类型转换 , 在 C 环境中只能使用 重新解释类型转换 reinterpret_cast ;
本篇博客中 , 分析 C 环境下 使用 各种方式 进行 父类 和 子类 类型之间的转换 , 推荐使用 动态类型转换 dynamic_cast ;
一、子类 和 父类 之间的类型转换 - 动态类型转换 dynamic_cast
C 面向对象 应用场景中 , 涉及到 父类 和 子类 之间的转换 ;
很明显 C 语言的 强制类型转换 , 不管是 隐式 还是 显示 转换 , 都无法转换 C 对象的类型 ;
动态类型转换 dynamic_cast 一般用于 父类 ( 对象 / 指针 / 引用 ) 和 子类 ( 对象 / 指针 / 引用 ) 之间的转换 , 是 C 语言特有的 , C 语言中没有该转换类型 ;
1、构造父类和子类
编写一个 父类 , 其中定义一个纯虚函数 ;
再编写两个 子类 , 重写 父类的 纯虚函数 , 每个子类再 各自定义一个 特有的函数 ;
代码语言:javascript复制// 父类
class Father
{
public:
virtual void say() = 0;
};
// 子类
class Son : public Father
{
public:
void say()
{
cout << "Son" << endl;
}
// Son 子类的特有函数
void son_say()
{
cout << "son_say" << endl;
}
};
// 子类2
class Son2 : public Father
{
public:
void say()
{
cout << "Son2" << endl;
}
// Son2 子类的特有函数
void son2_say()
{
cout << "son2_say" << endl;
}
};
2、子类 和 父类 之间的类型转换 - 隐式类型转换
先创建 子类对象 ,
将子类对象的 地址赋值给 父类指针 , 其中包含了 隐式转换 ;
在下面的代码中 , 使用取地址符获取 Son 类型 子类对象的地址 , 指针类型是 Son* 类型 , 将该类型值 赋值给 Father* 指针 , 其中进行了 隐式类型转换 ;
代码语言:javascript复制 Son son;
// 创建父类指针 , 直接让父类指针指向子类对象
// 不会报错 , 但是这么做有一定的风险
Father* pFather = NULL;
// 隐式类型转换
pFather = &son;
此外 , 函数接收 父类指针形参 作为参数 , 如果调用该函数 , 传入子类指针 , 此时涉及到将 子类指针 Son* 隐式转为 父类指针 Father* ;
代码语言:javascript复制// 函数接收 父类对象 作为参数, 可传入子类对象
void objSay(Father* obj)
{
}
// 调用函数, 传入子类对象指针
objSay(&son);
3、子类 和 父类 之间的类型转换 - 静态类型转换 static_cast
静态类型转换 static_cast , 可以在 C 编译器 编译时 对类型转换 进行检查 ;
如果 转换的类型不匹配 , 就会在编译时报错 , 避免出现更大的错误 ;
下面的代码中 , 使用取地址运算符 &son 获取 的 Son* 类型的 指针 , 将其使用 静态类型转换 static_cast 转为 Father* 类型的指针 ,
在 C 编译器编译阶段 , 会对类型进行检测 , 如果通过检测 , 则可以编译成功 , 如果类型错误 , 则会出现编译时报错的情况 ;
代码语言:javascript复制 Son son;
// 创建父类指针 , 直接让父类指针指向子类对象
// 不会报错 , 但是这么做有一定的风险
Father* pFather = NULL;
// 静态类型转换 static_cast, 可以在编译时 对类型转换 进行检查
pFather = static_cast<Father*>(&son);
下面的代码就是 执行静态类型转换 检查出错的情况 ,
Son 和 Son2 都是 Father 的子类 , 二者之间不能相互转化 , 只能是 父类 和 子类 之间进行相互转换 ;
类型转换错误报错 : error C2440: “static_cast”: 无法从“Son *”转换为“Son2 *” ;
代码语言:javascript复制 Son son;
Son2 son2;
// 创建父类指针 , 直接让父类指针指向子类对象
// 不会报错 , 但是这么做有一定的风险
Father* pFather = NULL;
// 静态类型转换 static_cast, 可以在编译时 对类型转换 进行检查
pFather = static_cast<Father*>(&son);
// 类型转换错误报错 : error C2440: “static_cast”: 无法从“Son *”转换为“Son2 *”
// message : 与指向的类型无关;
// 强制转换要求 reinterpret_cast、C 样式强制转换或函数样式强制转换
Son2* pSon2 = static_cast<Son2*>(&son);
执行后 , 出现如下报错 :
代码语言:javascript复制已启动生成…
1>------ 已启动生成: 项目: HelloWorld, 配置: Debug Win32 ------
1>Test.cpp
1>D: 02_Project 06_Visual_StudioHelloWorldHelloWorldTest.cpp(92,39): error C2440: “static_cast”: 无法从“Son *”转换为“Son2 *”
1>D: 02_Project 06_Visual_StudioHelloWorldHelloWorldTest.cpp(92,16): message : 与指向的类型无关;强制转换要求 reinterpret_cast、C 样式强制转换或函数样式强制转换
1>已完成生成项目“HelloWorld.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
完整代码示例 :
代码语言:javascript复制#include "iostream"
using namespace std;
// 父类
class Father
{
public:
virtual void say() = 0;
};
// 子类
class Son : public Father
{
public:
void say()
{
cout << "Son" << endl;
}
// Son 子类的特有函数
void son_say()
{
cout << "son_say" << endl;
}
};
// 子类2
class Son2 : public Father
{
public:
void say()
{
cout << "Son2" << endl;
}
// Son2 子类的特有函数
void son2_say()
{
cout << "son2_say" << endl;
}
};
// 函数接收 父类对象 作为参数, 可传入子类对象
void objSay(Father* obj)
{
// 调用 父类 纯虚函数 可发生多态调用
// 传入不同的子类 调用的是不同的函数
obj->say();
}
int main() {
Son son;
Son2 son2;
// 创建父类指针 , 直接让父类指针指向子类对象
// 不会报错 , 但是这么做有一定的风险
Father* pFather = NULL;
// 静态类型转换 static_cast, 可以在编译时 对类型转换 进行检查
pFather = static_cast<Father*>(&son);
// 类型转换错误报错 : error C2440: “static_cast”: 无法从“Son *”转换为“Son2 *”
// message : 与指向的类型无关;
// 强制转换要求 reinterpret_cast、C 样式强制转换或函数样式强制转换
Son2* pSon2 = static_cast<Son2*>(&son);
// 控制台暂停 , 按任意键继续向后执行
system("pause");
return 0;
};
执行结果 :
代码语言:javascript复制已启动生成…
1>------ 已启动生成: 项目: HelloWorld, 配置: Debug Win32 ------
1>Test.cpp
1>D: 02_Project 06_Visual_StudioHelloWorldHelloWorldTest.cpp(92,39): error C2440: “static_cast”: 无法从“Son *”转换为“Son2 *”
1>D: 02_Project 06_Visual_StudioHelloWorldHelloWorldTest.cpp(92,16): message : 与指向的类型无关;强制转换要求 reinterpret_cast、C 样式强制转换或函数样式强制转换
1>已完成生成项目“HelloWorld.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
4、子类 和 父类 之间的类型转换 - 重新解释类型转换 reinterpret_cast
C 中 父类 和 子类 之间类型转换 , 还可以使用 重新解释类型转换 reinterpret_cast ;
下面的代码中 , 将 Son* 指针类型 重新解释为 Father* 指针类型 ;
代码语言:javascript复制 // C 强制类型转换 , 重新解释类型转换 reinterpret_cast
pFather = reinterpret_cast<Father*>(&son);
pFather->say();
5、子类 和 父类 之间的类型转换 - 动态类型转换 dynamic_cast
动态类型转换 dynamic_cast , 一般用于 子类 和 父类 之间的类型转换 ,
- 运行时 , 如果类型转换成功 , 则进行转换 ;
- 运行时 , 如果类型转换失败 , 则返回转换结果 NULL ;
借助上述特性 , 动态类型转换 dynamic_cast 可用于在 运行时 识别对象类型 ;
将 对象 强转为 指定类型对象, 如果失败了, 转换结果为 NULL , 说明被转换的对象 不是 指定类型的对象 ;
下面代码的作用是 : 将Father* obj 父类对象 强转为 Son* 子类对象 ,
- 如果转换成功, 说明 obj 对象就是 Son 子类对象 , 则执行 Son 子类对象特有的函数 ;
- 如果转换失败, 说明不是 Son 子类对象, 转换结果是 NULL , 也就是 0 , 后续不再处理 ;
// 将Father* obj 父类对象 强转为 Son* 子类对象
// 如果转换成功, 说明 obj 对象就是 Son 子类对象
// 如果转换失败, 说明不是 Son 子类对象, 转换结果是 NULL , 也就是 0
Son* son = dynamic_cast<Son*>(obj);
if (son != NULL)
{
// 转换成功
// 执行 Son 特有工作
son->son_say();
}
完整代码 , 参考下面章节的 完整代码示例 ;
二、完整代码示例
1、代码示例
代码语言:javascript复制#include "iostream"
using namespace std;
// 父类
class Father
{
public:
virtual void say() = 0;
};
// 子类
class Son : public Father
{
public:
void say()
{
cout << "Son" << endl;
}
// Son 子类的特有函数
void son_say()
{
cout << "son_say" << endl;
}
};
// 子类2
class Son2 : public Father
{
public:
void say()
{
cout << "Son2" << endl;
}
// Son2 子类的特有函数
void son2_say()
{
cout << "son2_say" << endl;
}
};
// 函数接收 父类对象 作为参数, 可传入子类对象
void objSay(Father* obj)
{
// 调用 父类 纯虚函数 可发生多态调用
// 传入不同的子类 调用的是不同的函数
obj->say();
// 动态类型转换 dynamic_cast
// 可用于在 运行时 识别对象类型
// 将 对象 强转为 指定类型对象, 如果失败了, 转换结果为 NULL
// 将Father* obj 父类对象 强转为 Son* 子类对象
// 如果转换成功, 说明 obj 对象就是 Son 子类对象
// 如果转换失败, 说明不是 Son 子类对象, 转换结果是 NULL , 也就是 0
Son* son = dynamic_cast<Son*>(obj);
if (son != NULL)
{
// 转换成功
// 执行 Son 特有工作
son->son_say();
}
// 将Father* obj 父类对象 强转为 Son2* 子类对象
// 如果转换成功, 说明 obj 对象就是 Son2 子类对象
// 如果转换失败, 说明不是 Son2 子类对象, 转换结果是 NULL , 也就是 0
Son2* son2 = dynamic_cast<Son2*>(obj);
if (son2 != NULL)
{
// 转换成功
// 执行 Son2 特有工作
son2->son2_say();
}
}
int main() {
Son son;
Son2 son2;
// 创建父类指针 , 直接让父类指针指向子类对象
// 不会报错 , 但是这么做有一定的风险
Father* pFather = NULL;
// 静态类型转换 static_cast, 可以在编译时 对类型转换 进行检查
pFather = static_cast<Father*>(&son);
// 类型转换错误报错 : error C2440: “static_cast”: 无法从“Son *”转换为“Son2 *”
// message : 与指向的类型无关;
// 强制转换要求 reinterpret_cast、C 样式强制转换或函数样式强制转换
//Son2* pSon2 = static_cast<Son2*>(&son);
// C 强制类型转换 , 重新解释类型转换 reinterpret_cast
pFather = reinterpret_cast<Father*>(&son);
pFather->say();
// 动态类型转换示例
objSay(&son);
objSay(&son2);
// 控制台暂停 , 按任意键继续向后执行
system("pause");
return 0;
};
2、执行结果
执行结果 :
Son Son son_say Son2 son2_say Press any key to continue . . .