第八章 强制类型转换
c++提供了 隐式类型转换,所谓隐式类型转换,是指不需要用户干预,编译器默认进行的类型转换行为(很多时候用户可能都不知道到底进行了哪些转换)。例如:
int nValue = 8;
double dValue = 10.7;
// nValue会被自动转换为double类型,用转换的结果再与dValue相加
double dSum = nValue + dValue;
但是很多时候我们希望在表达式中明确指定将一种类型转换为另一种类型,这种转换方式我们称之为显示类型转换。隐式类型转换一般是由编译器进行转换操作,显示类型转换是由程序员写明要转换成的目标类型。显示类型转换又被称为强制类型转换。
c++提供了四种强制类型转换操作符:static_cast、const_cast、dynamic_cast、reinterpret_cast
8.1 const_cast
const_cast是一种C++运算符,主要是用来去除复合类型中const和volatile属性(没有真正去除)
变量本身的const属性是不能去除的,要想修改变量的值,一般是去除指针(或引用)的const属性,再进行间接修改。
用法:const_cast
<type>
(expression)
通过const_cast运算符,也只能将 const type *转换为type *, 将const type&转换为type&。
也就是说源类型和目标类型除了const属性不同,其他地方完全相同。
我们先来看这样一段代码:
#include <iostream>
using namespace std;
int main()
{
const int data = 100;
int *pp = (int *)&data;
*pp = 300;
cout << "data = " << data << "\t address: " << &data << endl << endl;
cout << " pp = " << *pp << "\t address: " << pp << endl << endl;
int *p = const_cast<int*>(&data);
cout << "data = " << data << "\t address: " << &data << endl << endl;
cout << "p = " << *p << "\t address: " << p << endl << endl;
*p = 200;
cout << "data = " << data << "\t address: " << &data << endl << endl;
cout << "p = " << *p << "\t address: " << p << endl << endl;
return 0;
}
data 的地址是 0x6ffdfc, p 指向的地址也是 0x6ffdfc, 但是修改 p 之后, 同一个地址上的内容却不相同。
可能是 const 的问题? const 的机制,就是在编译期间,用一个常量代替了 data。这种方式叫做常量折叠。
常量折叠与编译器的工作原理有关,是编译器的一种编译优化。在编译器进行语法分析的时候,将常量表达式计算求值,并用求得的值来替换表达式,放入常量表。所以在上面的例子中,编译器在优化的过程中,会把碰到的data(为const常量)全部以内容100替换掉,跟宏的替换有点类似。
常量折叠只对原生类型起作用,对我们自定义的类型,是不会起作用的
#include <iostream>
#include <stdio.h>
using namespace std;
class Test {
public:
int a;
Test() {
a = 10;
};
};
int main()
{
const Test b;
Test *b1 = const_cast<Test *>(&b);
cout << "b = " << b.a << endl;
b1->a = 100;
cout << "b = " << b.a << ", *b1 = " << b1->a << endl;
return 0;
}
函数的形参为非const,当我们传递一个const修饰的变量时可以通过const_cast去除该变量的const属性。
#include <iostream>
using namespace std;
const int *f(int *t)
{
int *p = new int;
*p = 100;
return p;
}
int main () {
int x = 1;
int *p1 = const_cast<int *>(f(&x));
*p1 = 1000;
const int *y = new int(10);
//int *p2 = const_cast<int *>(f(y));//[Note] in passing argument 1 of 'const int* f(int&)'
int *p2 = const_cast<int *>(f(const_cast<int *>(y)));
cout << *y <<endl;
// *y = 100; //[Error] assignment of read-only location '* y'
}
const_cast也可以去除函数返回值的const属性
8.2 static_cast
1、static_cast 主要用于内置数据类型之间的相互转换
double dValue = 12.12;
float fValue = 3.14; //从“double”到“float”截断
int nDValue = static_cast<int>(dValue); // 12
int nFValue = static_cast<int>(fValue); // 3
2、也可以转换自定义类型。如果涉及到类,static_cast只能在有相互联系(继承)的类型间进行转换,且不一定包含虚函数
class A
{};
class B : public A
{
public:
void f();
};
class C
{};
int main()
{
A *pA = new A;
B *pB = static_cast<B*>(pA); // 编译不会报错, B类继承于A类
pB = new B;
pA = static_cast<A*>(pB); // 编译不会报错, B类继承于A类
// 编译报错, C类与A类没有任何关系。error C2440: “static_cast”: 无法从“A *”转换为“C *”
C *pC = static_cast<C*>(pA);
A *p1;
B b2;
p1 = &b2;
static_cast<B *>(p1)->f();
}
8.3 dynamic_cast
dynamic_cast是四个强制类型转换操作符中最特殊的一个,它支持运行时识别指针或引用。
dynamic_cast用于类继承层次间的指针或引用转换。主要还是用于执行“安全的向下转型(safe downcasting)”,也即是基类对象的指针或引用转换为同一继承层次的其他指针或引用。
至于“向上转型”(即派生类指针或引用类型转换为其基类类型),本身就是安全的,尽管可以使用dynamic_cast进行转换,但这是没必要的, 普通的转换已经可以达到目的。
"하향 변환"의 전제 조건: 변환된 개체는 다형성 유형이어야 합니다 (다른 클래스에서 공개적으로 상속되거나 가상 함수가 있어야 함).
#include <iostream>
using namespace std;
class Base
{
public:
Base(){};
virtual void Show(){cout<<"This is Base calss";}
};
class Derived:public Base
{
public:
Derived(){};
void Show(){cout<<"This is Derived class";}
};
int main()
{
Base *base ;
Derived *der = new Derived;
//base = dynamic_cast<Base*>(der); //正确,但不必要。
base = der; //先上转换总是安全的
base->Show();
}
"하향 변환" 에는 두 가지 경우가 있습니다 .
-
하나는 기본 클래스 포인터가 가리키는 객체가 파생 클래스 유형이고 이 변환이 안전하다는 것입니다.
-
다른 하나는 기본 클래스 포인터가 가리키는 객체가 기본 클래스 유형이라는 것입니다.이 경우 dynamic_cast는 런타임에 확인하고 변환에 실패하며 반환 결과는 0입니다.
#include <iostream>
using namespace std;
class Base
{
public:
Base(){};
virtual void Show(){cout<<"This is Base calss" << endl;}
};
class Derived:public Base
{
public:
Derived(){};
void Show(){cout<<"This is Derived class" << endl;}
void d_f()
{cout << "d_f" << endl;}
};
class Derived1:public Base
{
public:
Derived1(){};
void Show(){cout<<"This is Derived1 class" << endl;}
void d_f()
{cout << "d1_f" << endl;}
};
void f(Base *p)
{
Derived *d;
if (d = dynamic_cast<Derived *>(p))
{
d->d_f();
d->Show();
}
}
int main()
{
Derived d1;
Derived1 d2;
f(&d2);
Base *p = new Derived;
f(p);
Base *p1 = new Base;
f(p1);
#if 0
Derived d;
Base *p = new Derived;
//Derived *pd = static_cast<Derived *>(p);
Derived *pd = dynamic_cast<Derived *>(p);
pd->Show(); //派生类中的函数
Base *p1 = new Base;
//pd = dynamic_cast<Derived *>(p1); //运行返回值为0
pd = static_cast<Derived *>(p1);
pd->Show(); //基类中的函数
pd->d_f();
#endif
}
8.4 reinterpret_cast
1. reinterpret_cast는 다양한 유형의 포인터 간, 서로 다른 유형의 참조 간, 포인터와 포인터를 수용할 수 있는 정수 유형 간 변환에 사용됩니다. 변환할 때 비트 단위 복사 작업이 수행됩니다.
2. 이 변환은 뛰어난 유연성을 제공하지만 변환의 보안은 프로그래머의 주의에 의해서만 보장될 수 있습니다. 예를 들어 프로그래머가 int* 포인터, 함수 포인터 또는 기타 유형의 포인터를 string* 유형 포인터로 변환해야 한다고 주장하는 것도 가능합니다. 버그를 찾는 지루한 작업을 스스로 수행할 수 있습니다. 함수 포인터를 개체 포인터로 변환하는 것을 허용하지 않지만 Visual Studio 2010과 같은 일부 컴파일러는 이 변환을 지원합니다.)
#include <iostream>
using namespace std;
class A
{
public:
int i;
int j;
A(int n):i(n),j(n) { }
};
int main()
{
A a(100);
int &r = reinterpret_cast<int&>(a); //强行让 r 引用 a
r = 200; //把 a.i 变成了 200
cout << a.i << "," << a.j << endl; // 输出 200,100
int n = 300;
A *pa = reinterpret_cast<A*> ( & n); //强行让 pa 指向 n
pa->i = 400; // n 变成 400
pa->j = 500; //此条语句不安全,很可能导致程序崩溃
cout << n << endl; // 输出 400
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 pf1; PF2 pf2;
pf2 = reinterpret_cast<PF2>(pf1); //两个不同类型的函数指针之间可以互相转换
}