c++的类型转换(转)
类型转换机制可以分为:隐式类型转换 和 显示类型转换(强制类型转换)
C中的类型转换:
事情要从头说起,这个头就是C语言.我们已经习惯了使用C-like类型转换,因为它强大而且简单.
主要有以下两种形式:
- (new-type) expression
- new-type (expression)
C++中的类型转换:
隐式类型转换比较常见,在混合类型表达式中经常发生。比如在表达式中存在short和int,那么就过会发生整型提升.四种强制类型转换操作符:static_cast、dynamic_cast、const_cast、reinterpret_cast。
1. static_cast<>与dynamic_cast<>:
把这两个放在一起比较容易记忆,"一静一动".从字面上也可以看出,前者提供的是编译时期的静态类型检测,后者提供的是
运行时检测.
- static_cast: 1)完成基础数据类型,2)同一个继承体系中类型的转换 3)任意类型与空指针类型void*之间的转换。
- dynamic_cast:使用多态的场景,增加了一层对真实调用对象类型的检查
char c = 65;
int *p = (int *)&c;//c like强制类型转换临时转变为int *,系统中变量c的类型未变
cout<<(char)*p<<endl;//'A',同理强制类型转换,int to char
*p = 5;
int *q = static_cast<int *>(&c); //编译报错:error: invalid static_cast from type ‘char*’ to type ‘int*’
在上面的例子中,C like可以运行,然而使用c++方式将会出现错误,static_cast可以将错误在编译时期检查出来。
在不同继承体系的自定义类型中:
class A
{
public:
A(){}
~A(){} private:
int i, j;
}; class C
{
public:
C(){}
~C(){} void printC()
{
std::cout <<"call printC() in class C" <<std::endl;
}
private:
char c1, c2;
}; A *ptrA = new A();
C *ptrC = (C *)(ptrA);//强制类型转换
ptrC->printC(); //"call printC() in class C"
//ptrC = static_cast<C*>(ptrA); //编译报错:error: invalid static_cast from type 'A*’ to type C*’
delete ptrA;
上面A和C是两个无关的类,不存在继承关系,然而使用C-like可以实现这种类型的临时强制转换,这是十分危险的! 那么使用static_cast可以将这种潜在的危险在编译器中找出来。那如何使用static_cast呢?
在同一继承体系中,存在
upcast(向上转换即子类转成父类、派生类转为基类):没有问题,因为父类(基类)的行为都包含在子类(派生类)中;
downcast(向下转换即基类转成派生类):有可能会出现问题,编译时可能不会发现。
以下举一个带有多态的继承例子说明该问题:
#include <iostream>
#include <cstdio> using namespace std;
class A
{
public:
A():i(1), j(1){}
~A(){} void printA()
{
std::cout <<"call printA() in class A" <<std::endl;
} void printSum()
{
std::cout <<"sum = " <<i+j <<std::endl;
} private:
int i, j;
}; class B : public A//公有继承
{
public:
B():a(2), b(2) {}
~B(){} void printB()
{
std::cout <<"call printB() in class B" <<std::endl;
} void printSum()
{
std::cout <<"sum = " <<a+b <<std::endl;
} void Add()
{
a++;
b++;
} private:
double a, b;
};
int main()
{
B *ptrB = new B;
ptrB -> printSum();//sum=4
A *ptrA = static_cast<A *>(ptrB);//upcast,派生类转化为基类,也可以写成A *ptrA = static_cast<B *>(ptrB)
ptrA -> printA();
ptrA -> printSum(); //打印结果:sum = 2
//在进行upcast的时候,指针指向的对象的行为与指针的类型相关。 ptrA = new A;
ptrB = static_cast<B *>(ptrA); //downcast,基类转化为派生类
ptrB -> printB();
ptrB -> printSum(); //打印结果:sum等于未知的数
//在进行downcast的时候,其行为是“undefined”。 B b;
B &rB = b;
rB.printSum(); //打印结果: sum = 4
A &rA = static_cast<A &>(b);
rA.printA();
rA.printSum(); //打印结果: sum = 2
//在进行upcast的时候,指针指向的对象的行为与引用类型相关. A a;
A &rA1 = a;
rA.printSum(); //打印结果:sum=2
B &rB1 = static_cast<B &>(a);
rB1.printB();rB1.printSum();//打印结果sum未知
//在进行downcast的时候,其行为是“undefined”。
return 0;
}
这里其实很明显,一个类的行为和自身的类型相关,也就是一个A类型的指针总会优先调用自己A类内的函数(编译器看到的是指针的类型或引用的类型,这是早绑定,即静态多态的例子),当然发生继承中的重写(虚继承等)(编译器看到的是指针指向的内容或引用的变量,这是晚绑定,即动态多态的例子)例外。在downcast转换的时候,会出现一些跟指针或者引用类型相关的函数调用,但是因为指针或者引用(基类)没有定义这些行为,因为调用到了这些行为导致出现了未定义的行为,这是在继承关系中存在的转换关系。
而解决这个问题的办法就是,虚函数! 如果声明A类中的printSum为虚函数,那么子类B就会有一个虚表,虚表中的第一个函数就是printSum函数,也就是B类的该函数(即晚绑定)。所以A类指针调用该函数就会调用B类中的该函数,显示结果sum= 4。在未定义之前sum = 2(A类中的该函数)。另外声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用操作就是对目标变量操作。指针和引用最明显的区别是引用必须要初始化,且从始至终只能指向定义的那个目标变量,其值只能通过目标变量本身的改变而改变,试图改变引用名的行为是非法的,反之指针的值可变。
总之,就是尽可能不要使用downcast,也就是使用派生类的指针指向基类。
dynamic_cast<>
1.dynamic_cast是在运行时检查的,用于在集成体系中进行安全的向下转换downcast(当然也可以向上转换,但没必要,因为可以用虚函数实现)
即:基类指针/引用 -> 派生类指针/引用
如果源和目标没有继承/被继承关系,编译器会报错!
2.dynamic_cast是4个转换中唯一的RTTI操作符,提供运行时类型检查。
3.dynamic_cast不是强制转换,而是带有某种”咨询“性质的,如果不能转换,返回NULL。这是强制转换做不到的。
4.源类中必须要有虚函数,保证多态,才能使用dynamic_cast<source>(expression)
static_cast<>
用法:static_cast < type-id > ( expression )
该运算符把expression转换为type-id类型,在编译时使用类型信息执行转换,在转换执行必要的检测(指针越界,类型检查),其操作数相对是安全的。
但没有运行时类型检查来保证转换的安全性。
reinterpret_cast<>
通常为操作数的位模式提供较低层的重新解释。
const_cast<>
在进行类型转换时用来修改类型的const或volatile属性,除了const或volatile修饰之外,原来的数据值和数据类型都是不变的。
c++的类型转换(转)的更多相关文章
- 为C# as 类型转换及Assembly.LoadFrom埋坑!
背景: 不久前,我发布了一个调试工具:发布:.NET开发人员必备的可视化调试工具(你值的拥有) 效果是这样的: 之后,有小部分用户反映,工具用不了(没反应或有异常)~~~ 然后,建议小部分用户换个电脑 ...
- c# 基础 object ,new操作符,类型转换
参考页面: http://www.yuanjiaocheng.net/webapi/config-webapi.html http://www.yuanjiaocheng.net/webapi/web ...
- Struts2日期类型转换
针对日期类java.util.Date进行类型转换,要求客户端使用"yyyy-MM-dd","yyyy/MM/dd"中的任意一种输入,并以"yyyy- ...
- 【.NET深呼吸】基础:自定义类型转换
照例,老周在开始吹牛之前,先讲讲小故事,这是朋友提出的建议,老TMD写技术有什么了不起的,人人都会写.后来老周想想,也确实,代码谁不会写,能写到有品位有感悟,就不容易做到.于是,老周接受了该朋友的建议 ...
- C++四种类型转换方式。
类型转换有c风格的,当然还有c++风格的.c风格的转换的格式很简单(TYPE)EXPRESSION,但是c风格的类型转换有不少的缺点,有的时候用c风格的转换是不合适的,因为它可以在任意类型之间转换,比 ...
- struts2类型转换
1. Struts2中的类型转换 我们知道通过HTTP提交到后台的数据,都是字符串的形式,而我们需要的数据类型当然不只字符串类型一种.所以,我们需要类型转换! 在Struts2中,类型转换的概念除了用 ...
- C++_系列自学课程_第_11_课_类型转换_《C++ Primer 第四版》
上次说了关于表达式的一些内容,说到还有一些关于数据类型转换的内容,今天我们接着八一八C++中的数据类型转换. 一.隐式类型转换 在表达式中,有些操作符可以对多种类型的操作数进行操作, 例如 + 操作符 ...
- Struts2入门(三)——数据类型转换
一.前言 笔者一直觉得,学习一个知识点,你首先要明白,这东西是什么?有什么用?这样你才能了解.好了,不说废话. 1.1.类型转换为何存在?什么是类型转换? 在MVC框架中,都是属于表示层解决方案,都需 ...
- js条件判断时隐式类型转换
Javascript 中,数字 0 为假,非0 均为真 在条件判断运算 == 中的转换规则是这样的: 如果比较的两者中有布尔值(Boolean),会把 Boolean 先转换为对应的 Number,即 ...
- JavaScript中数据类型转换总结
JavaScript中数据类型转换总结 在js中,数据类型转换分为显式数据类型转换和隐式数据类型转换. 1, 显式数据类型转换 a:转数字: 1)Number转换: 代码: var a = " ...
随机推荐
- vsftpd重启失败解决方法
vsftpd dead,but subsys locked vsftpd已死,但是subsys被锁 关于VSFTPD服务器重启失败,研究了一晚上,当virtual_use_local_privs=ye ...
- 最小费用流spfa算法模板(pascal)
以前写过,现在的码风与以前有些变化,主要是用数组模拟邻接表存图,以前是用指针存图. 以前的博文:http://www.cnblogs.com/Currier/p/6387732.html 洛谷可评测. ...
- P4329 [COCI2006-2007#1] Bond
题意翻译 有n 个人去执行n 个任务,每个人执行每个任务有不同的成功率,每个人只能执行一个任务,求所有任务都执行的总的成功率. 输入第一行,一个整数n (1≤n≤20 ),表示人数兼任务数.接下来n ...
- 【BZOJ4205】卡牌配对
Description 现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C.把卡牌分为X,Y两类,分别有n1,n2张. 两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属性值互质,且 ...
- 洛谷 P1777 帮助_NOI导刊2010提高(03) 解题报告
P1777 帮助_NOI导刊2010提高(03) 题目描述 Bubu的书架乱成一团了!帮他一下吧! 他的书架上一共有n本书.我们定义混乱值是连续相同高度书本的段数.例如,如果书的高度是30,30,31 ...
- 构建工具----gradle---可能遇到的问题----Could not reserve enough space for object heap
Could not reserve enough space for object heap 意思是 jvm的设置内存不足以运行gradle命令了. 分为两种情况,解决的方法也不同. .10/user ...
- String,static,final
1. String 下面代码创建了几个对象? String s1 = new String("Hello"); String s2 = new String("Hello ...
- 前端学习 -- Css -- 兄弟元素选择器
为一个元素后边的元素设置css样式: 语法:前一个 + 后一个. 作用:可以选中一个元素后紧挨着的指定的兄弟元素. 为一个元素后边的所有相同元素设置css样式: 语法:前一个 ~ 后边所有. < ...
- android app 的插件化、组件化、模块化开发
Android 插件化 ——指将一个程序划分为不同的部分,比如一般 App的皮肤样式就可以看成一个插件 Android 组件化 ——这个概念实际跟上面相差不那么明显,组件和插件较大的区别就是:组件是指 ...
- 解题:USACO12FEB Nearby Cows
题面 比较简单的树形dp(递推?) 设$dp[i][j]$表示距离$i$距离为$j$的点的数目,先预处理$g[i][j]$表示点$i$的子树中距离这个点距离为$j$的点的数目(猫老师讲过,用一个栈维护 ...