【C++】类型转换 ④ ( 子类 和 父类 之间的类型转换 - 动态类型转换 dynamic_cast )

2023-11-28 11:22:11 浏览数 (3)

在之前写过一篇 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_Project06_Visual_StudioHelloWorldHelloWorldTest.cpp(92,39): error C2440: “static_cast”: 无法从“Son *”转换为“Son2 *”
1>D:02_Project06_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_Project06_Visual_StudioHelloWorldHelloWorldTest.cpp(92,39): error C2440: “static_cast”: 无法从“Son *”转换为“Son2 *”
1>D:02_Project06_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 , 后续不再处理 ;
代码语言:javascript复制
	// 将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 . . .

0 人点赞