c++基础——强制类型转换符

(一)概念:

隐式类型转换是编译器自动隐式进行的,需要在代码中体现,而显式类型转换由程序员明确指定。

C++支持C风格的强制转换(将类型名作为强制类型转换运算符的做法是C语言的老式做法),但是C风格的强制转换可能带来一些隐患,让一些问题难以发现。

所以C++ 引入了四种功能不同的强制类型转换运算符以进行强制类型转换。

强制转换运算符是一种特殊的运算符,它把一种数据类型转换为另一种数据类型。强制转换运算符是一元运算符,它的优先级与其他一元运算符相同。

大多数的 C++ 编译器都支持大部分通用的强制转换运算符。

形式为:**强制类型转换运算符<type> (expression) **

type为转换后的数据类型,expression为待转换的表达式

c++的四种强制类型转换运算符分别为:static_cast、reinterpret_cast、const_cast和dynamic_cast。

(二)风险:

强制类型转换是有一定风险的,有的转换并不一定安全,如把整型数值转换成指针,把基类指针转换成派生类指针,把一种函数指针转换成另一种函数指针,把常量指针转换成非常量指针等。

C++ 引入新的强制类型转换机制,主要是为了克服C语言强制类型转换的以下三个缺点。

  • 没有从形式上体现转换功能和风险的不同。

例如,将int强制转换成double是没有风险的,而将常量指针转换成非常量指针,将基类指针转换成派生类指针都是高风险的,而且后两者带来的风险不同(即可能引发不同种类的错误),C语言的强制类型转换形式对这些不同并不加以区分。

  • 将多态基类指针转换成派生类指针时不检查安全性,即无法判断转换后的指针是否确实指向一个派生类对象。

  • 难以在程序中寻找到底什么地方进行了强制类型转换。

    强制类型转换是引发程序运行时错误的一个原因,因此在程序出错时,可能就会想到是不是有哪些强制类型转换出了问题。

如果采用C语言的老式做法,要在程序中找出所有进行了强制类型转换的地方,显然是很麻烦的,因为这些转换没有统一的格式。

而用 C++ 的方式,则只需要查找_cast字符串就可以了。甚至可以根据错误的类型,有针对性地专门查找某一种强制类型转换。例如,怀疑一个错误可能是由于使用了reinterpret_cast导致的,就可以只查找reinterpret_cast字符串。

(三)四种转换操作:

1. const_cast:

const_cast 运算符用于修改类型的const /volatile属性。除了const或volatile属性之外,目标类型必须与源类型相同(即type和expression结果类型相同)。这种类型的转换主要是用来操作所传对象的const属性,可以加上const属性,也可以去掉const属性(常用)。

#include <iostream>
using namespace std;
int main() {
	const string s = "Inception";
	string& p = const_cast <string&> (s);
	string* ps = const_cast <string*> (&s);
	p += "1";
	*ps += "2";
	cout << typeid(p).name() << endl;
	cout << typeid(ps).name() << endl;
	cout << p << endl;
	return 0;
}
//运行结果:
//class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> >
//class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> >* __ptr64
//Inception12
2. reinterpret_cast:

reinterpret_cast 运算符用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个比特复制的操作。

reinterpret_cast 功能十分强大,可以实现任意类型之间的转换。

这个转换是“最不安全”的。不推荐使用。

#include <iostream>
using namespace std;
class A
{
public:
	int i;
	int j;
	A(int n1,int n2) :i(n1), j(n2) { }
};
void show1(int) { cout << "show1" << endl; return; }
int show2(int, char*) { cout << "show2" << endl; return 1; }
int main()
{
	A a(100,200);

	//强行让r引用a
	int& r = reinterpret_cast<int&>(a); 
	r = 300;  //把a.i变成了300
	std::cout << a.i << "," << a.j << std::endl;  // 输出 300,200

	//强行让pa指向n
	int n = 400;
	A* pa = reinterpret_cast<A*> (&n); 
	pa->i = 500;  // n变成400
	//pa->j = 600;  //此条语句越界操作内存,不安全,会导致程序崩溃
	cout << n << endl;  // 输出 500

	long long la = 0x12345678abcdLL;
	pa = reinterpret_cast<A*>(la); //la太长,只取低32位0x5678abcd拷贝给pa
	unsigned int u = reinterpret_cast<unsigned int>(pa);//pa逐个比特拷贝到u
	cout << hex << u << endl;  //输出 5678abcd

	typedef void (*PF1) (int);
	typedef int (*PF2) (int, char*);
	//两个不同类型的函数指针之间可以互相转换,但是pf1和pf2必须初始化
	PF1 pf1=show1;  PF2 pf2=show2;
	pf1(1); //show1
	cout << pf2(1, (char*)("1")) << endl; //show2,返回1
	pf2 = reinterpret_cast<PF2>(pf1);
	pf1(1); //show1
	cout << pf2(1, (char*)("1")) << endl; //show1,返回1,(表现非常奇怪,不安全)
	return 0;
}
//运行结果:
//300, 200
//500
//5678abcd
//show1
//show2
//1
//show1
//show1
//b0e74080
3. static_cast:

编译器隐式执行的任何类型转换都可以由static_cast显式完成。

static_cast主要用于基本数据类型之间的转换,如把char转换为 int,把int转换为double等。

使用static_cast可以明确告诉编译器,损失精度的转换是在知情的情况下进行的,也可以让阅读程序的其他程序员明确转换的目的而不是由于疏忽。

把精度大的类型转换为精度小的类型,static_cast使用位截断进行处理。

static_cast执行的是非动态转换,没有运行时的类型检查来保证转换的安全性。

例如,对于类层次的转换:

  • static_cast 进行上行转换是安全的,即把派生类的指针转换为基类的;
  • static_cast 进行下行转换是不安全的,即把基类的指针转换为派生类的。

使用static_cast可以找回存放在void*指针中的值。

static_cast可以把任何类型的表达式转换成void*类型。

#include <iostream>
using namespace std;
class A
{
public:
	int i;
	int j;
	operator int() { return 1; }
	operator char* () { return NULL; }
};
class B :public A 
{
public:
	int z;
};
int main()
{
	A a;
	int n;
	char* p = (char*)"New Dragon Inn";
	n = static_cast <int> (a);     //调用 a.operator int,n的值变为1
	cout << n << endl;
	p = static_cast <char*> (a);   //调用 a.operator char*,p的值变为NULL
	cout << &p << endl;
	n = static_cast <int> (3.14);  //转换丢失精度,n的值变为3
	cout << n << endl;
	int x = -1;
	unsigned int y = static_cast<unsigned int>(x); // 转换为无符号整数最大值,此处为4294967295
	cout << y << endl;
	//n = static_cast <int> (p);  //编译错误,static_cast不能将指针转换成整型
	//p = static_cast <char*> (n);  //编译错误,static_cast 不能将整型转换成指针

	double da = 2.991;
	void* vp = static_cast<void*>(&da);    //double转void*类型,void*指向double类型值
	double* dp = static_cast<double*>(vp); //将void*类型的double值找回来
	cout << *dp << endl;   //输出2.991
	A* pa = new A;
	B* pb = new B;
	pa->i = 1;
	pa->j = 2;
	pb->i = 10;
	pb->j = 20;
	pb->z = 30;
	A* pab = static_cast<B*>(pb); //上行转换,派生类指针->基类指针,pab的i和j的值与pb的相同,z的值丢弃
	cout << pab->i << "," << pab->j << endl;
	B* pba = static_cast<B*>(pa); //下行转换,基类指针->派生类指针,不进行类型检查,不安全,i和j的值与pa的相同,但是z的值相当于未初始化
	cout << pba->z << "," << pba->i << "," << pba->j << endl;
	return 0;
}
4. dynamic_cast:

dynamic_cast 主要用于类层次间的上行转换或安全的下行转换(实际就是专门用于将多态基类指针或引用转换为派生类指针或引用)。在进行上行转换时,dynamic_cast 和 static_cast 的效果是一样的,但在下行转换时,dynamic_cast 具有类型检查的功能,比 static_cast 更安全。

对于“向下转型”有两种情况:

  • 基类指针所指对象是派生类类型的,这种情况下将其转换为派生类指针转换是安全的;
  • 基类指针所指对象为基类类型,在这种情况下dynamic_cast在运行时做检查,转换失败,返回结果为NULL指针;

dynamic_cast最特殊的地方在于它支持运行时识别指针或引用。

dynamic_cast 是在运行时执行转换,进行类型检查的。如果转换未执行,则转换失败,表达式 expr 被判定为 null。dynamic_cast 执行动态转换时,type 必须是类的指针、类的引用或者 void*,如果 type 是类指针类型,那么 expr 也必须是一个指针,如果 type 是一个引用,那么 expr 也必须是一个引用。

dynamic_cast检查的来源是虚函数表。

#include <iostream>
using namespace std;

class Base {
public:
	int i;
	virtual void Show() {
		cout << "I am Base." << endl;
	}
};

class Derive : public Base {
public:
	int j;
	virtual void Show() {
		cout << "I am Derive." << endl;
	}
};

int main()
{
	// 上行转换
	Derive* d1 = new Derive();
	cout << "d1: " << d1 << endl;
	Base* b1 = dynamic_cast<Base*>(d1); //转换安全,且无影响。
	cout << "b1: " << b1 << endl;

	// 下行转换
	//基类指针指向派生类类型进行转换
	Base* b2 = new Derive;
	cout << "b2: " << b2 << endl;
	if (Derive* d2 = dynamic_cast<Derive*>(b2))
	{
		cout << "第一种情况转换成功" << endl;
		cout << "d2: " << d2 << endl;
		d2->Show();
	}
	else cout << "第一种情况转换失败" << endl;
	//基类指针指向基类类型进行转换
	Base* b3 = new Base;
	cout << "b3: " << b3 << endl;
	if (Derive* d3 = dynamic_cast<Derive*>(b3))
	{
		cout << "第二种情况转换成功" << endl;
		d3->Show();
	}
	else
	{
		cout << "第二种情况转换失败" << endl;
		cout << "d3: " << d3 << endl;
	}
	delete b1;
	delete b2;
	delete b3;

	//引用在其他方面表现和指针一样,但因为没有NULL类型,不能用dynamic_cast强制转换时抛出错误。
	Base bbb;
	Base& bbbb = bbb;
	//Derive& dddd = dynamic_cast<Derive&>(bbbb); 
	return 0;
}
//运行结果:
//d1 : 000002531E58F8C0
//b1 : 000002531E58F8C0
//b2 : 000002531E58F740
//第一种情况转换成功
//d2 : 000002531E58F740
//I am Derive.
//b3 : 000002531E592440
//第二种情况转换失败
//d3 : 0000000000000000
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
谭浩强教授,我国著名计算机教育专家。1934年生。1958年清华大学毕业。学生时代曾担任清华大学学生会主席、北京市人民代表。他是我国计算机普及和高校计算机基础教育开拓者之一,现任全国高等院校计算机基础教育研究会会长、教育部全国计算机应用技术证书考试委员会主任委员。 谭浩强教授创造了3个世界纪录:(1)20年来他(及和他人合作)共编著出版了130本计算机著作,此外主编了250多本计算机书籍,是出版科技著作数量最多的人。(2)他编著和主编的书发行量超过4500万册,是读者最多的科技作家。我国平均每30人、知识分子每1.5人就拥有1本谭浩强教授编著的书。(3)他和别人合作编著的《BASIC语言》发行了1200万册,创科技书籍发行量的世界纪录。此外,他编著的《C程序设计》发行了600万册。他曾在中央电视台主讲了BASIC,FORTRAN,COBOL,Pascal,QBASIC,C,Visual Basic七种计算机语言,观众超过300万人。 在我国学习计算机的人中很少有不知道谭浩强教授的。他善于用容易理解的方法和语言说明复杂的概念。许多人认为他开创了计算机书籍贴近大众的新风,为我国的计算机普及事业做出了重要的贡献。 谭浩强教授曾获全国高校教学成果国家级奖、国家科技进步奖,以及北京市政府授予的“有突出贡献专家”称号。《计算机世界》报组织的“世纪评选”把他评为我国“20世纪最有影响的IT人物”10个人之一(排在第2位)。他的功绩是把千百万群众带入计算机的大门。 1 C语言概述 1.1 C语言的发展过程 1.2 当代最优秀的程序设计语言 1.3 C语言版本 1.4 C语言的特点 1.5 面向对象的程序设计语言 1.6 C和C++ 1.7 简单的C程序介绍 1.8 输入和输出函数 1.9 C源程序的结构特点 1.10 书写程序时应遵循的规则 1.11 C语言的字集 1.12 C语言词汇 1.13 Turbo C 2.0 集成开发环境的使用 1.13.1 Turbo C 2.0 简介和启动 1.13.2 Turbo C 2.0 集成开发环境 1.13.3 File菜单 1.13.4 Edit 菜单 1.13.5 Run 菜单 1.13.6 Compile 菜单 11.13.7 Project 菜单 1.13.8 Options菜单 1.13.9 Debug 菜单 1.13.10 Break/watch 菜单 1.13.11 Turbo C 2.0 的配置文件 2 程序的灵魂—算法 2.1 算法的概念 21 2.2 简单算法举例 21 2.3 算法的特性 24 2.4 怎样表示一个算法 24 2.4.1 用自然语言表示算法 24 2.4.2 用流程图表示算法 24 2.4.3 三种基本结构和改进的流程图 28 2.4.4 用N-S 流程图表示算法 29 2.4.5 用伪代码表示算法 30 2.4.6 用计算机语言表示算法 31 2.5 结构化程序设计方法 31 3 数据类型、运算符与表达式 3.1 C语言的数据类型 32 3.2 常量与变量 33 23.2.1 常量和号常量 33 3.2.2 变量 33 3.3 整型数据 34 3.3.1 整型常量的表示方法 34 3.3.2 整型变量 35 3.4 实型数据 37 3.4.1 实型常量的表示方法 37 3.4.2 实型变量 38 3.4.3 实型常数的类型 39 3.5 字型数据 39 3.5.1 字常量 39 3.5.2 转义字 39 3.5.3 字变量 40 3.5.4 字数据在内存中的存储形式及使用方法 41 3.5.5 字串常量 41 3.5.6 号常量 42 3.6 变量赋初值 42 3.7 各类数值型数据之间的混合运算 43 3.8 算术运算符和算术表达式 44 3.8.1 C运算符简介 44 3.8.2 算术运算符和算术表达式 45 3.9 赋值运算符和赋值表达式 47 33.10 逗号运算符和逗号表达式 48 3.11 小结 49 3.11.1 C的数据类型 49 3.11.2 基本类型的分类及特点 49 3.11.3 常量后缀 49 3.11.4 常量类型 49 3.11.5 数据类型转换 49 3.11.6 运算符优先级和结合性 50 表达式 50 4 最简单的 C程序设计—顺序程序设计 4.1 C语句概述 51 4.2 赋值语句 53 4.3 数据输入输出的概念及在 C 语言中的实现 54 4.4 字数据的输入输出 54 4.4.1 putchar 函数(字输出函数) 54 4.4.2 getchar函数(键盘输入函数) 55 4.5 格式输入与输出 55 4.5.1 printf 函数(格式输出函数) 56 4.5.2 scanf函数(格式输入函数)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaohei07

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值