- 浏览: 526618 次
文章分类
最新评论
-
chaodilei:
我可以提个意见吗?作为前端工程师,写博客应该注意段落标题的ba ...
我的前端之路 -
zhuchao_ko:
大家一起二。
我的前端之路
一个VC编译错误引发的对显示类型转换的思考(static_cast、dynamic_cast和const_cast)
一、问题提出
今天在研究effective c++中碰到的copy构造函数的时候,运行了下面这个程序,编译出现了错误:
#include<iostream> using namespace std; class point { private: int m_x,m_y; public: point() { m_x = 0; m_y = 0; } point(int x, int y) { m_x = x; m_y = y; } point(const point& p) { m_x = p.m_x; m_y = p.m_y; cout<<"copy constructor is called!"<<endl; } static point reverse(const point& p) { point p1; p1.m_x = p.getY(); p1.m_y = p.getX(); return p1; } int getX() { return m_x; } int getY() { return m_y; } void print() { cout<<m_x<<" "<<m_y<<endl; } }; int main() { point p(1, 2); point p1(p); //initialize p1 with p point p2 = point::reverse(p1); p2.print(); return 0; }
错误信息为:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Compiling...
item3.cpp
D:\Project\EffctiveCProj1\item3.cpp(32) : error C2662: 'getY' : cannot convert 'this' pointer from 'const class point' to 'class point &'
Conversion loses qualifiers
D:\Project\EffctiveCProj1\item3.cpp(33) : error C2662: 'getX' : cannot convert 'this' pointer from 'const class point' to 'class point &'
Conversion loses qualifiers
Error executing cl.exe.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二、问题分析
分析了一下错误提示,发现是不能将类型从const class point转为class point &,定位到代码,原来出现在line21和line22 getY()和getX()调用这里:
p1.m_x = p.getY();
p1.m_y = p.getX();
p是一个const point&类型,而getY()和getX()都是非const类型,按照C++的规定,const常量无法调用非const函数,只可以调用const函数,因为const常量对象的数据成员是不允许改变的,非const函数就有可能改变数据成员,因此不允许调用!
三、问题解决
既然明白了这点,就很好修改了,两种方式:
A、将getX()和getY()改成const函数(简单)
const函数只需要在函数定义的地方的参数列表之后花括号之前加上const即可
B、将p转换为非const变量(稍微复杂)
在《effective c++》的“item 03:尽可能的使用const”一章中,作者介绍了如何将const类型转换为非const类型,即:采用static_cast或者const_cast.
按照plan A的修改:
#include<iostream> using namespace std; class point { private: int m_x,m_y; public: point() { m_x = 0; m_y = 0; } point(int x, int y) { m_x = x; m_y = y; } point(const point& p) { m_x = p.m_x; m_y = p.m_y; cout<<"copy constructor is called!"<<endl; } static point reverse(const point& p) { point p1; p1.m_x = p.getY(); p1.m_y = p.getX(); return p1; } int getX() const //const函数 { return m_x; } int getY() const //const函数 { return m_y; } void print() { cout<<m_x<<" "<<m_y<<endl; } }; int main() { point p(1, 2); point p1(p); //initialize p1 with p point p2 = point::reverse(p1); p2.print(); return 0; }
bingo,编译通过!
按照plan B的修改:
#include<iostream> using namespace std; class point { private: int m_x,m_y; public: point() { m_x = 0; m_y = 0; } point(int x, int y) { m_x = x; m_y = y; } point(const point& p) { m_x = p.m_x; m_y = p.m_y; cout<<"copy constructor is called!"<<endl; } static point reverse(const point& p) { point p1; p1.m_x = static_cast<point>(p).getY(); //将const point类型显示转换为point类型 p1.m_y = static_cast<point>(p).getX(); //将const point类型显示转换为point类型 return p1; } int getX() { return m_x; } int getY() { return m_y; } void print() { cout<<m_x<<" "<<m_y<<endl; } }; int main() { point p(1, 2); point p1(p); //initialize p1 with p point p2 = point::reverse(p1); p2.print(); return 0; }
同样编译通过了!
对于plan B,同样可以采用const_cast进行类型转换:
// CastExample.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include<iostream> using namespace std; class point { private: int m_x,m_y; public: point() { m_x = 0; m_y = 0; } point(int x, int y) { m_x = x; m_y = y; } point(const point& p) { m_x = p.m_x; m_y = p.m_y; cout<<"copy constructor is called!"<<endl; } static point reverse(const point& p) { point p1; p1.m_x = const_cast<point&>(p).getY(); //将const point类型显示转换为point类型 p1.m_y = const_cast<point&>(p).getX(); //将const point类型显示转换为point类型 return p1; } int getX() { return m_x; } int getY() { return m_y; } void print() { cout<<m_x<<" "<<m_y<<endl; } }; int main() { point p(1, 2); point p1(p); //initialize p1 with p point p2 = point::reverse(p1); p2.print(); return 0; }
注意:在使用const_cast进行转换的时候,尖括号中的参数必须是引用类型,即:const_cast<point&>(p).getY()
四、进一步探讨(关于C++类型转换)
1.隐式类型转换
又称为“标准转换”或者“自动类型转换”,由编译系统自动完成,对程序员透明。包括以下几种情况:
1) 算术转换(Arithmetic conversion) : 在混合类型的算术表达式中, 最宽的数据类型成为目标转换类型。
int ival = 3;
double dval = 3.14159;
ival + dval;//ival被提升为double类型,结果是个double类型
2)一种类型表达式赋值给另一种类型的对象:目标类型是被赋值对象的类型
int *pi = 0; // 0被转化为int *类型
ival = dval; // double->int
例外:void指针赋值给其他指定类型指针时,不存在标准转换,编译出错
3)将一个表达式作为实参传递给函数调用,此时形参和实参类型不一致:目标转换类型为形参的类型
extern double sqrt(double);
cout << "The square root of 2 is " << sqrt(2) << endl;//2被提升为double类型:2.0
4)从一个函数返回一个表达式,表达式类型与返回类型不一致:目标转换类型为函数的返回类型
double difference(int ival1, int ival2)
{
return ival1 - ival2; //返回值被提升为double类型
}
2.显式类型转换
又称“强制类型转换”(cast),由程序员完成。
C++标准定义了四个类型转换符:reinterpret_cast, static_cast, dynamic_cast 和 const_cast,目的在于控制类(class)之间的类型转换。
(1)static_cast
用法:static_cast <type-id> ( expression )
说明:该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。做这些转换前,你必须确定要转换的数据确实是目标类型的数据,因为static_cast不做运行时的类型检查以保证转换的安全性。也因此,static_cast不如dynamic_cast安全。对含有二义性的指针,dynamic_cast会转换失败,而static_cast却直接且粗暴地进行转换。这是非常危险的。
使用场景:
a、用于类层次结构中基类和子类之间指针或引用的转换。
进行上行转换(把子类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类型检查,所以是不安全的。
b、用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
d、把任何类型的表达式转换成void类型。注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。举例:a、用于类层次结构中基类和子类之间指针或引用的转换。
// CastExample.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include<iostream> using namespace std; class Point { protected: int m_x,m_y; public: Point() { m_x = 0; m_y = 0; } Point(int x, int y) { m_x = x; m_y = y; } void print() { cout<<m_x<<" "<<m_y<<endl; } }; class Point3d : public Point { private: int m_z; public: Point3d(int x, int y, int z):Point(x, y),m_z(z){} void print() { cout<<m_x<<" "<<m_y<<" "<<m_z<<endl; } }; void f(Point* p1, Point3d* p2) { Point* pa = static_cast<Point*>(p2); //safe Point3d* pb = static_cast<Point3d*>(p1);//unsafe pa->print(); pb->print(); } int main() { Point p(1, 2); Point3d p3d(1, 2, 3); f(&p, &p3d); return 0; }
运行结果为:
虽然编译能够通过,但是运行出的结果很明显是不正确的!
b、在两个类对象之间进行转换,比如把类型为A的对象a,转换为类型为B的对象。如下:
#include "stdafx.h" #include<iostream> #include<string> using namespace std; class Student { public: string name; int age; string sno; public: Student(string name = "", int age = 20, string sno = ""):name(name),age(age),sno(sno){} void print() { cout<<"name = "<<name<<" age = "<<age<<" sno = "<<sno<<endl; } }; class Teacher { private: string name; int age; string tno; public: Teacher(string name = "", int age = 20, string tno = ""):name(name),age(age),tno(tno){} Teacher(const Student& stu) { name = stu.name; age = stu.age; tno = stu.sno; } void print() { cout<<"name = "<<name<<" age = "<<age<<" tno = "<<tno<<endl; } }; int main() { Student stu("wanjun", 24, "12345"); Teacher tea = static_cast<Teacher>(stu); tea.print(); }
如果让以上代码通过编译,那么Teacher类必须含有以Student类的对象(或对象的引用)为参数的构造函数。即:
Teacher(const Student& stu) { name = stu.name; age = stu.age; tno = stu.sno; }这实际上是把转换的工作交给构造函数去做了。
// CastExample.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include<iostream> using namespace std; class Point { public: int m_x,m_y; public: Point() { m_x = 0; m_y = 0; } Point(int x, int y) { m_x = x; m_y = y; } void print() { cout<<m_x<<" "<<m_y<<endl; } }; class Point3d : public Point { private: int m_z; public: Point3d(int x, int y, int z):Point(x, y),m_z(z){} void print() { cout<<m_x<<" "<<m_y<<" "<<m_z<<endl; } }; void f(Point p1, Point3d p2) { Point pa = static_cast<Point>(p2); Point3d pb = static_cast<Point3d>(p1); // 出现error pa.print(); pb.print(); } int main() { Point p1(1, 2); Point3d p2(1, 2, 3); f(p1, p2); return 0; }
// CastExample.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include<iostream> using namespace std; class Point { public: int m_x,m_y; public: Point() { m_x = 0; m_y = 0; } Point(int x, int y) { m_x = x; m_y = y; } void print() { cout<<m_x<<" "<<m_y<<endl; } }; class Point3d : public Point { private: int m_z; public: Point3d(int x, int y, int z):Point(x, y),m_z(z){} Point3d(const Point& p) //copy 构造函数 { m_x = p.m_x; m_y = p.m_y; m_z = p.m_x + p.m_y; } void print() { cout<<m_x<<" "<<m_y<<" "<<m_z<<endl; } }; void f(Point p1, Point3d p2) { Point pa = static_cast<Point>(p2); Point3d pb = static_cast<Point3d>(p1); pa.print(); // 输出1、2 pb.print(); // 输出1、2、3 } int main() { Point p1(1, 2); Point3d p2(1, 2, 3); f(p1, p2); return 0; }运行成功,而且结果没有出现例a中的“随机值”!
(2)dynamic_cast
说明:该运算符把expression转换成type-id类型的对象。
使用场景:
a、dynamic_cast主要用于类层次间的上行转换和下行转换
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
把上面的例a改为采用dynamic_cast 的方式,并做适当修改:
// CastExample.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include<iostream> using namespace std; class Point { public: int m_x,m_y; public: virtual void foo(){}; Point() { m_x = 0; m_y = 0; } Point(int x, int y) { m_x = x; m_y = y; } void print() { cout<<m_x<<" "<<m_y<<endl; } }; class Point3d : public Point { private: int m_z; public: Point3d(int x, int y, int z):Point(x, y),m_z(z){} void print() { cout<<m_x<<" "<<m_y<<" "<<m_z<<endl; } void foo() { } }; void f(Point *p1, Point3d *p2) { Point *pa = dynamic_cast<Point*>(p2); Point3d *pb = dynamic_cast<Point3d*>(p1); // pb is null if (pa) { pa->print(); } if (pb) { pb->print(); } } int main() { Point p1(1, 2); Point3d p2(1, 2, 3); f(&p1, &p2); return 0; }在上面的代码段中,将子类对象p2转为父类对象pa是安全的,而将父类对象p1转为子类对象pb也是安全的,因为pb将是一个空指针(即0,因为dynamic_cast失败)。
另外要注意:Base要有虚函数,否则会编译出错;static_cast则没有这个限制。
这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。
dynamic_cast支持交叉转换(cross cast)如下代码所示:
class Base { public: int m_iNum; virtual void f(){} }; class Derived1 : public Base { }; class Derived2 : public Base { }; void foo() { derived1 *pd1 = new Drived1; pd1->m_iNum = 100; Derived2 *pd2 = static_cast<Derived2 *>(pd1); //compile error Derived2 *pd2 = dynamic_cast<Derived2 *>(pd1); //pd2 is NULL delete pd1; }
在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。
(3)const_cast
用法:const_cast<type_id>(expression)
说明:该运算符用来修改类型的const或volatile属性。除了const或volatile修饰之外,type_id和expression的类型是一样的。
使用场景:
a、常量指针被转化成非常量指针,并且仍然指向原来的对象;
b、常量引用被转换成非常量引用,并且仍然指向原来的对象;
c、常量对象被转换成非常量对象。
Voiatile和const类似。
// CastExample.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include<iostream> using namespace std; class point { private: int m_x,m_y; public: point() { m_x = 0; m_y = 0; } point(int x, int y) { m_x = x; m_y = y; } point(const point& p) { m_x = p.m_x; m_y = p.m_y; cout<<"copy constructor is called!"<<endl; } static point reverse(const point& p) { point p1, p2; p2 = const_cast<point&>(p); //将const point类型显示转换为point类型 p1.m_x = p2.getY(); p1.m_y = p2.getX(); return p1; } int getX() { return m_x; } int getY() { return m_y; } void print() { cout<<m_x<<" "<<m_y<<endl; } }; int main() { point p(1, 2); point p1(p); //initialize p1 with p point p2 = point::reverse(p1); p2.print(); return 0; }
注意<>和()中的类型一定要一致,都为引用或者都为指针或都为对象!
五、小结
C++的四种强制转型形式每一种适用于特定的目的:
·dynamic_cast 主要用于执行“安全的向下转型(safe downcasting)”,也就是说,要确定一个对象是否是一个继承体系中的一个特定类型。它是唯一不能用旧风格语法执行的强制转型,也是唯一可能有重大运行时代价的强制转型。
·static_cast 可以被用于强制隐型转换(例如,non-const 对象转型为 const 对象,int 转型为 double,等等),它还可以用于很多这样的转换的反向转换(例如,void* 指针转型为有类型指针,基类指针转型为派生类指针),但是它不能将一个
const 对象转型为 non-const 对象(只有 const_cast 能做到),它最接近于C-style的转换。
·const_cast 一般用于强制消除对象的常量性。它是唯一能做到这一点的 C++ 风格的强制转型。
·reinterpret_cast 是特意用于底层的强制转型,导致实现依赖(implementation-dependent)(就是说,不可移植)的结果,例如,将一个指针转型为一个整数。这样的强制转型在底层代码以外应该极为罕见。
(2013年11月14日 0:02)
相关推荐
标准C++的类型转换符:static_cast、dynamic_cast、reinterpret_cast和const_cast
标准C++的类型转换符:static_cast、dynamic_cast、reinterpret_cast和const_cast
例说 const_cast,reinterpret_cast,static_cast,dynamic_cast代码.rar
主要介绍了C++中的类型转换static_cast、dynamic_cast、const_cast和reinterpret_cast总结,需要的朋友可以参考下
使用标准C++的类型转换符:static_cast、dynamic_cast、reinterpret_cast和const_cast。 const_cast,字面上理解就是去const属性。 static_cast,命名上理解是静态类型转换。如int转换成char。 dynamic_cast,...
2.dynamic_cast提供安全的转换如果两个指针不存在继承关系转换会失败返回空指针,如果你提供一个错误的指针那样会发生内存访问异常,因为它会去比较两个类型的虚函数表。虚函数表的指针一般放在对象指针最开始的四字...
C-style cast举例: int i;...下面是一个简单的程序: 代码如下:#include <iostream>using namespace std;int main(void){ int i; double d = 11.29; i = (int)d; cout << i << endl; co
呵呵,今天来好好看看着几个转换操作符的用法。以前老是看着眼熟,但是用着手生。今天决定搞定这些个东西。在C语言中类型转换有几种方式:1.(expression).在表达式外边加括号,由编译器来决定怎么改变。2.new_type...
主要介绍了C++中const_cast与reinterpret_cast运算符的用法,经常被用于表达式中的类型转换,需要的朋友可以参考下
为了⽀持这样的⽤法,shared_ptr提供了类似的转型函数 static_pointer_cast()、const_pointer_cast()、dynamic_pointer_cast(),它们与标准的转型操作符static_cast()、const_cast()和dynamic_cast()类似,但返回的...
本文详细介绍了C++中的四个用与强制类型转换的运算符:用来修改类型的const 或volatile 属性的const_cast,用来修改操作数类型的reinterpret_cast,static_cast,dynamic_cast
xxx_cast是一个统称,它指的是static_cast(静态转换),const_cast(常量转换),reinterpert_cast(重解释转换),dynamic_cast(动态转换)。本次我们就了解一下他的用途及其转换方式。 1.static_cast(静态转换) ...
C++风格的类型转换提供了4种类型转换操作符来应对不同场合的应用。
C++中的类型转换static_cast、dynamic_cast、const_cast和reinterpret_cast
什么是static_cast,dynamic_cast以及reinterpret_cast?区别是什么?为什么要注意? A:转换的含义是通过改变一个变量的类型为别的类型从而改变该变量的表示方式。为了类型转换一个简单对象为另一个对象你会使用传统...
C++其实也具有自己的一套强制类型转换它们分明是:static_cast reinterpret_cast const_cast dynamic_cast四种类型. 那么肯定会有人好奇C++是不是闲,C语言的强制类型用的舒舒服服的,为什么要新推出来这几个? 新...