c++四种强制类型转换

C++ 类型转换(C风格的强制转换):

在C++基本的数据类型中,可以分为四类:整型,浮点型,字符型,布尔型。其中数值型包括 整型与浮点型;字符型即为char。

(1)将浮点型数据赋值给整型变量时,舍弃其小数部分。

(2)将整型数据赋值给浮点型变量时,数值不变,但是以指数形式存储。

(3)将double型数据赋值给float型变量时,注意数值范围溢出。

(4)字符型数据可以赋值给整型变量,此时存入的是字符的ASCII码。

(5)将一个int,short或long型数据赋值给一个char型变量,只将低8位原封不动的送到char型变量中。
(6)将有符号型数据赋值给长度相同的无符号型变量,连同原来的符号位一起传送。

C++强制类型转换:

在C++语言中新增了四个关键字static_cast、const_cast、reinterpret_cast和dynamic_cast。这四个关键字都是用于强制类型转换的。

新类型的强制转换可以提供更好的控制强制转换过程,允许控制各种不同种类的强制转换。

C++中风格是static_cast(content)。C++风格的强制转换其他的好处是,它们能更清晰的表明它们要干什么。程序员只要扫一眼这样的代码,就能立即知道一个强制转换的目的。

1) static_cast

在C++语言中static_cast用于数据类型的强制转换,强制将一种数据类型转换为另一种数据类型。例如将整型数据转换为浮点型数据。
[例1]C语言所采用的类型转换方式:

int a = 10;
int b = 3;
double result = (double)a / (double)b;

例1中将整型变量a和b转换为双精度浮点型,然后相除。在C++语言中,我们可以采用static_cast关键字来进行强制类型转换,如下所示。
[例2]static_cast关键字的使用:

int a = 10;
int b = 3;
double result = static_cast(a) / static_cast(b);

在本例中同样是将整型变量a转换为双精度浮点型。采用static_cast进行强制数据类型转换时,将想要转换成的数据类型放到尖括号中,将待转换的变量或表达式放在元括号中,其格式可以概括为如下形式:

用法:static_cast <类型说明符> (变量或表达式)

它主要有如下几种用法:
(1)用于类层次结构中基类和派生类之间指针或引用的转换
进行上行转换(把派生类的指针或引用转换成基类表示)是安全的
进行下行转换(把基类的指针或引用转换为派生类表示),由于没有动态类型检查,所以是不安全的
(2)用于基本数据类型之间的转换,如把int转换成char。这种转换的安全也要开发人员来保证
(3)把空指针转换成目标类型的空指针
(4)把任何类型的表达式转换为void类型
注意:static_cast不能转换掉expression的const、volitale或者__unaligned属性。

static_cast:可以实现C++中内置基本数据类型之间的相互转换。

如果涉及到类的话,static_cast只能在有相互联系的类型中进行相互转换,不一定包含虚函数。

2) const_cast

在C语言中,const限定符通常被用来限定变量,用于表示该变量的值不能被修改。

而const_cast则正是用于强制去掉这种不能被修改的常数特性,但需要特别注意的是const_cast不是用于去除变量的常量性,而是去除指向常数对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用。

用法:const_cast (expression)
该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。
常量指针被转化成非常量指针,并且仍然指向原来的对象;
常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。

[例3]一个错误的例子:

const int a = 10;
const int * p = &a;
*p = 20; //compile error
int b = const_cast(a); //compile error

在本例中出现了两个编译错误,第一个编译错误是*p因为具有常量性,其值是不能被修改的;另一处错误是const_cast强制转换对象必须为指针或引用,而例3中为一个变量,这是不允许的!

[例4]const_cast关键字的使用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

include

using namespace std;

int main()

{

const int a = 10;

const int * p = &a;

int *q;

q = const_cast<int *>(p);

*q = 20;    //fine

cout <<a<<" "<<*p<<" "<<*q<<endl;

    cout <<&a<<" "<<p<<" "<<q<<endl;

return 0;

}

在本例中,我们将变量a声明为常量变量,同时声明了一个const指针指向该变量(此时如果声明一个普通指针指向该常量变量的话是不允许的,Visual Studio 2010编译器会报错)。

之后我们定义了一个普通的指针*q。将p指针通过const_cast去掉其常量性,并赋给q指针。之后我再修改q指针所指地址的值时,这是不会有问题的。

最后将结果打印出来,运行结果如下:
10 20 20
002CFAF4 002CFAF4 002CFAF4

查看运行结果,问题来了,指针p和指针q都是指向a变量的,指向地址相同,而且经过调试发现002CFAF4地址内的值确实由10被修改成了20,这是怎么一回事呢?为什么a的值打印出来还是10呢?

其实这是一件好事,我们要庆幸a变量最终的值没有变成20!变量a一开始就被声明为一个常量变量,不管后面的程序怎么处理,它就是一个常量,就是不会变化的。试想一下如果这个变量a最终变成了20会有什么后果呢?对于这些简短的程序而言,如果最后a变成了20,我们会一眼看出是q指针修改了,但是一旦一个项目工程非常庞大的时候,在程序某个地方出现了一个q这样的指针,它可以修改常量a,这是一件很可怕的事情的,可以说是一个程序的漏洞,毕竟将变量a声明为常量就是不希望修改它,如果后面能修改,这就太恐怖了。

在例4中我们称“*q=20”语句为未定义行为语句,所谓的未定义行为是指在标准的C++规范中并没有明确规定这种语句的具体行为,该语句的具体行为由编译器来自行决定如何处理。对于这种未定义行为的语句我们应该尽量予以避免!

从例4中我们可以看出我们是不想修改变量a的值的,既然如此,定义一个const_cast关键字强制去掉指针的常量性到底有什么用呢?我们接着来看下面的例子。

例5:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

include

using namespace std;

const int * Search(const int * a, int n, int val);

int main()

{

int a[10] = {0,1,2,3,4,5,6,7,8,9};

int val = 5;

int *p;

p = const_cast<int *>(Search(a, 10, val));

if(p == NULL)

    cout<<"Not found the val in array a"<<endl;

else

    cout<<"hvae found the val in array a and the val = "<<*p<<endl;

return 0;

}

const int * Search(const int * a, int n, int val)

{

int i;

for(i=0; i<n; i++)

{

    if(a[i] == val)

        return &a[i];

}

return  NULL;

}

在例5中我们定义了一个函数,用于在a数组中寻找val值,如果找到了就返回该值的地址,如果没有找到则返回NULL。函数Search返回值是const指针,当我们在a数组中找到了val值的时候,我们会返回val的地址,最关键的是a数组在main函数中并不是const,因此即使我们去掉返回值的常量性有可能会造成a数组被修改,但是这也依然是安全的。

对于引用,我们同样能使用const_cast来强制去掉常量性,如例6所示。

例6:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

include

using namespace std;

const int & Search(const int * a, int n, int val);

int main()

{

int a[10] = {0,1,2,3,4,5,6,7,8,9};

int val = 5;

int &p = const_cast

include

using namespace std;

class base

{

public :

void m(){cout<<"m"<<endl;}

};

class derived : public base

{

public:

void f(){cout<<"f"<<endl;}

};

int main()

{

derived * p;

p = new base;

p = static_cast<derived *>(new base);

p->m();

p->f();

return 0;

}

本例中定义了两个类:base类和derived类,这两个类构成继承关系。在base类中定义了m函数,derived类中定义了f函数。在前面介绍多态时,我们一直是用基类指针指向派生类或基类对象,而本例则不同了。

本例主函数中定义的是一个派生类指针,当我们将其指向一个基类对象时,这是错误的,会导致编译错误。

但是通过强制类型转换我们可以将派生类指针指向一个基类对象,p = static_cast

include

using namespace std;

class base

{

public :

void m(){cout<<"m"<<endl;}

};

class derived : public base

{

public:

void f(){cout<<"f"<<endl;}

};

int main()

{

derived * p;

p = new base;

p = dynamic_cast<derived *>(new base);

p->m();

p->f();

return 0;

}

在本例中利用dynamic_cast进行强制类型转换,但是因为base类中并不存在虚函数,因此p = dynamic_cast

include

include

using namespace std;

class A

{

public:

virtual void f()

{

   cout<<"hello"<<endl;

   };

};

class B:public A

{

public:

void f()

{

    cout<<"hello2"<<endl;

    };

};

class C

{

void pp()

{

  return;

}

};

int fun()

{

return 1;

}

int main()

{

A* a1=new B;//a1是A类型的指针指向一个B类型的对象

A* a2=new A;//a2是A类型的指针指向一个A类型的对象

B* b;

C* c;

b=dynamic_cast<B*>(a1);//结果为not null,向下转换成功,a1之前指向的就是B类型的对象,所以可以转换成B类型的指针。

if(b==NULL)

{

    cout<<"null"<<endl;

}



else

{

    cout<<"not null"<<endl;

}



b=dynamic_cast<B*>(a2);//结果为null,向下转换失败

if(b==NULL)

{

    cout<<"null"<<endl;

}



else

{

    cout<<"not null"<<endl;

}



c=dynamic_cast<C*>(a);//结果为null,向下转换失败

if(c==NULL)

{

    cout<<"null"<<endl;

}



else

{

    cout<<"not null"<<endl;

}



delete(a);

return 0;

}

猜你喜欢

转载自blog.csdn.net/caogenwangbaoqiang/article/details/80873278
今日推荐