C++ Primer 学习笔记与思考_10 类型转换易错处大总结

(一)隐式转换

先来看一组样例:

int ival=0;
ival=3.451+3;

首先做加法操作,操作数分别为int和double类型,c++并没有直接把两个数加在一起,而是提供了一组转换规则,以便在执行算术操作之前,将两个操作数转换为同一种数据类型。在这里是自动执行的,所以他们被成为隐式转换。

因为 C++定义了算术类型之间的内置转换以尽可能防止精度损失,所以这里3转换为double型相加得6.451。但是下一步赋值的时候,右操作数进行截断处理,对此精度会有损失,编译器也会给出警告。

2.常见的隐式转化

① 混合类型表达式中,操作数转换成相同的类型(如上例)

int ival=0;
double dval;
ival>=dval;

②用作条件的表达式转换成bool类型

int val;
if(val)
while(val)

③ 用一个表达式初始化某个变量,或将一个表达式赋值给某个变量,则该表达式被转换成变量类型。

int ival=3.21;
int *ip;
ip=0; // the int converted to a null pointer of type int *

3.由标准库类型定义的转换

string s;
while(cin>>s)

这里隐式使用了IO标准库定义的类型转换,返回的是cin,此处将istream类型的值转换为bool类型,意味这要检验流的状态,如果读取成功,返回true,如果失败,比如说读取到文件的末尾,那么转换为bool后为false;循环条件不成立。

(二)显式转换

①static_cast

用法:static_cast < type-id > ( expression )

说明:该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。

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

2 当需要将一个较大的算术类型转换成一个较小的类型时,使用强制转换特别有用,注意这个时候编译器不会再提示警告,因为这告诉编译器:我们知道并且不关心潜在的精度损失。

3. 对于编译器不提供自动转换的情况,使用static_cast来执行类型转换也是很有用的。

double d =5.6;
void* p=&d;
double *dp=static_cast<double*>(*p);

②const_cast

顾名思义,将转换掉表达式中的const性质。

常量指针被转化成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然指向原来的对象;

const char *pc;
char *p =const_cast<char*>(pc);
const int a=6;
int &b=const_cast<int&>(a);
int c=const_cast<int>(a); //error,具体对象之间是错误的 

③reiterpret_cast

reinterpret_cast是为了映射到一个完全不同类型的意思,这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄玄虚和其他目的,这是所有映射中最危险的。(这句话是C++编程思想中的原话).

也就是说我们仅仅是把int*看作了char*类型。

它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,再把该整数转换成原类型的指针,还可以得到原先的指针值)。

int *ip;
char *pc=reinterpret_cast<char*>(ip); //pc真实指向int型
string str(pc);// error ,int cannnot init string
double a=8.97;
double *p=&a;
int b=reinterpret_cast<int>(p);
cout<<b<<endl; 

错误的使用reinterpret_cast很容易导致程序的不安全,只有将转换后的类型值转换回到其原始类型,这样才是正确使用reinterpret_cast方式。不过,还是要谨慎的使用reinterpret_cast。

④ dynamic_cast

dynamic_cast运算符用于将基类类型的指针或者引用安全的转换为派生类型的指针或引用。他可以在执行期决定真正的类型。如果 downcast 是安全的(也就说,如果基类指针或者引用确实指向一个派生类对象)这个运算符会传回适当转型过的指针。如果 downcast 不安全,这个运算符会传回空指针(也就是说,基类指针或者引用没有指向一个派生类对象),对于引用来说则是抛出bad_cast类型的异常。

dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。

在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;

在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

另外要注意:A 要有虚函数,否则会编译出错;static_cast则没有这个限制。

这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。

 #include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
class A
{
	int a;
	virtual void p(){} //must
};
class B :public  A
{
	int b;

};
int main()
{
	B nameB;  A nameA;
	A *p1=&nameA;
	A* p = &nameB;
	B* cur = dynamic_cast<B*>(p); // OK p point to B
	B* cur1 = dynamic_cast<B*>(p1); //  error,NULL,p1没有指向B
	//cout << cur << endl;
	try
	{
		A &k = nameA;
		B &temp = dynamic_cast<B&>(k);
	}
	catch(bad_cast) {
		cout << "bad_cast" << endl;
	}

}

(三)旧式强制转换

标准C++提供的类型转换加强了转换的可视性和安全性,旧式强制转换依赖于所涉及的数据类型,具有和const_cast,static_cast和reinterpret_cast一样的行为。在合法使用static_cast或const_cast的地方,旧式强制转换提供了与各自对应的命名强制转换一样的功能。如果这两种转换均不合法,则旧式强制转换执行reinterpret_cast功能,如下:

int ival;double dval;
ival+=int(dval); // static_cast :converts double to int
const char* pc_str;
string_copy((char*)pc_str);// const_cast casts away const
int *ip;
char*pc=(char*)p; //reinterpret_cast :treats int* as char*

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-16 20:45:16

C++ Primer 学习笔记与思考_10 类型转换易错处大总结的相关文章

C++ Primer学习笔记与思考_1----变量和基本类型

C++ Primer学习笔记与思考_1----变量和基本类型 类型有什么作用? 类型告诉我们数据代表什么意思以及可以对数据执行哪些操作. 一:基本内置类型 C++标准规定了每个算术类型的最小存储空间,但是它并不阻止编译器使用更大的存储空间.对于int,几乎所有的编译器使用的存储空间都比所要求的大. 字符类型有两种char和wchar_t,其中wchar_t用于扩展字符集,比如汉字和日文,这些字符集中的一些字符不能用单个的char表示.  c++ primer第32页      和其他类型不同,c

C++ Primer 学习笔记与思考_7 void和void*指针的使用方法

(一)void的含义 void的字面意思是"无类型",void差点儿仅仅有"凝视"和限制程序的作用,由于从来没有人会定义一个void变量,让我们试着来定义: void a; 这行语句编译时会出错.提示"illegal use of type 'void'".只是.即使void a的编译不会出错.它也没有不论什么实际意义. void真正发挥的作用在于: (1) 对函数返回的限定: (2) 对函数參数的限定. int f(void); equal t

C++ Primer 学习笔记与思考_7 void和void*指针的用法

(一)void的含义 void的字面意思是"无类型",void几乎只有"注释"和限制程序的作用,因为从来没有人会定义一个void变量,让我们试着来定义: void a; 这行语句编译时会出错,提示"illegal use of type 'void'".不过,即使void a的编译不会出错,它也没有任何实际意义. void真正发挥的作用在于: (1) 对函数返回的限定: (2) 对函数参数的限定. int f(void); equal to in

C++ Primer 学习笔记与思考_9 表达式中过的那些容易忽略的地方

(一)移位操作符用于IO 输入输出标准库分别重载了位操作符>>和<<用于输入和输出.并且像其他的二元操作符一样,移位操作符也是左结合的,这也正好说明了我们为什么可以把多个输入输出操作连接为单个语句: cout<<"hi"<<"there "<<endl; 将其执行为: ( (cout<<"hi")<<"there" )<<endl;

C++ Primer 学习笔记与思考_4 ---getline&amp;&amp;size_type&amp;&amp;const_iterator混搭

(一)getline的用法 我们在使用string读入字符串的时候,其输入操作符: * 读取并忽略开头所有空白字符(如空格,换行符,制表符) * 读取字符直至再次遇到空白字符,读取终止. 所以如果我们输入"hello world",string将只输出hello. 为了解决这个问题,我们引入getline函数,它接受两个参数:一个输入流对象和一个string对象,但是getline会读取换行符,保存到string中时却忽略换行符,所以string中是空串. 我们通常用getchar()

C++ Primer 学习笔记与思考_3 ---头文件那些事儿(extern)

(一)extern在头文件里的使用方法 由于头文件包括在多个源文件里.而且变量的定义仅仅能出现一次,所以在头文件里. 仅仅能够声明不能够出现定义. 我们能够在头文件里用extern声明全局变量,这样在包括此头文件的cpp 中可直接使用 声明过的这个全局变量(例如以下面程序中的变量a). 可是有三个例外: 1.头文件里能够定义类 2.值在编译时就已经知道的const对象 3.能够定义inline函数 extern int ival; // yes extern int ival=1; //erro

C++ Primer 学习笔记与思考_5 bitset你用的正确吗?

bitset类型比整型值上的低级位操作更容易使用,简化了位集的处理.在定义bitset时,要明确bitset含有的位数,在尖括号内给出它的长度值. 注意位集合的编号从0开始,低位存低阶位. 最重要的一个问题:bitset的下标是从右边开始的!!!!很多人的博客都写错了,希望引起大家注意. 1.使用unsigned long值初始化bitset对象 若bitset类型长度大于unsigned long值的二进制位数,则其余的高阶位将置为0:若bitset类型长度小于unsigned long值的二

C++ Primer 学习笔记与思考_5 数组和动态数组易错点解读

1. 数组的定义 数组定义中的类型名可以是内置数据类型或者类类型:除了引用之外,数组元素的类型还可以是任意的复合类型.另外,非const变量以及要到运行阶段才知道其值的const变量都不能用于定义数组的维数. #include <iostream> #include <string> #include <cstring> #include <bitset> using namespace std; int main() { const int MAXN1 =

C++ Primer 学习笔记_28_操作符重载与转换(3)--成员函数的重载、覆盖与隐藏、类型转换运算符、*运算符重载、-&gt;运算符重载

C++ Primer 学习笔记_28_操作符重载与转换(3)--成员函数的重载.覆盖与隐藏.类型转换运算符.*运算符重载.->运算符重载 一.成员函数的重载.覆盖与隐藏 对于类层次的同名成员函数来说,有三种关系:重载.覆盖和隐藏,理清3种关系,有助于写出高质量的代码. 1.成员函数的重载 重载的概念相对简单,只有在同一类定义中的同名成员函数才存在重载关系,主要特点时函数的参数类型和数目有所不同:但不能出现函数参数的个数和类型均相同,仅仅依靠返回值类型不同来区分的函数,这和普通函数的重载是完全一致