c++中的四种类型转换
1.静态转换 : static_cast
用法:static_cast<type_name>(val)
1.基本类型间的转换
c++
enum Day { Mon = 1, Tues = 2, Wed = 3, Thu = 4, Fir = 5, Sat = 6, Sun = 7 };
int main()
{
int a = 10;
char ch = 't';
double dx = 21.65;
a = static_cast<int>(ch);
a = static_cast<int>(dx);
Day x = Mon;
a = x;
x = static_cast<Day>(a);
return 0;
}
2.指针转换
c++
int main()
{
int a = 10;
char b = 'a';
int* pa = &a;
char* pb = &b;
const int* pc = &a;
pa = (int*)pb; //c语言的强转,不安全
pa = static_cast<int*>(pb);//error 类型不符合
pa = (int*)pc; //c语言的强转,不安全
pa = static_cast<int*>(pc);//error 无法去除const属性
return 0;
}
总结:
1.不可以改变指针类型(特例:void型)
2.无法去除指针的const属性
3.对于void类型的指针,可以进行类型转换(如下)
c++
int main()
{
int a = 10;
int* ip = nullptr;
double* dp = nullptr;
void* vp = &a;
ip = static_cast<int*>(vp);
dp = static_cast<double*>(vp);
return 0;
}
3.左值到右值转换
c++
int main()
{
int a = 10;
int &x = a;
int &&rx = a; // error;
int &&rx = static_cast<int &&>(a);
return 0;
}
4.自定义类型间的转换
c++
class Base
{
private:
int a;
public:
Base(int x = 0) : a(x){}
};
class Obj : public Base
{
private:
int val;
public:
Obj(int x = 1) : val(x){}
};
class Int
{
int val;
public:
Int(int x = 2) : val(x){}
};
1. 不同类型间转换
c++
int main()
{
Base a;
Int b;
Base* pa = &a;
Int* pb = nullptr;
pb = (Int*)pa; //c语言的强转,不安全
pb = static_cast<Int*> (pa); //error 类型不一样
return 0;
}
和内置类型一样,不行
2.上行转换
C++
int main()
{
Base a;
Obj b;
Base* pa = &a;
Obj* pb = &b;
pa = pb; //隐式转换,(赋值兼容规则)
pa = static_cast<Base*>(pb);
a = b; //隐式转换,(赋值兼容规则)
a = static_cast<Base>(b);
return 0;
}
注意:
隐式执行任何类型转换都可由static_cast显示完成。
static_cast不能转换掉val的const属性。
3.下行转换
c++
int main()
{
Base a;
Obj b;
Base* pa = &a;
Obj* pb = &b;
pb = pa; //error ,不满足赋值兼容规则
pb = static_cast<Obj*>(pa);
// pb->val 为随机值
return 0;
}
会发生越界问题
2.去除常性转换 const_cast
用法:const_cast <type_name>(val)
特点:
1) 用于去除变量的只读属性
2) 强制转换的目标类型必须是指针或引用
1.内置类型
C++
int main()
{
const int a = 22;
const int* pb = &a;
const int& b = a;
int* pc = const_cast<int*> (pb);
*pc = 100;
cout<<"a = "<<a<<" *pc = "<<*pc<<endl;
int& la = const_cast<int&> (b);
cout<<"a = "<<a<<" la = "<<la<<endl;
int&& ra = const_cast<int&&> (b);
cout<<"b = "<<b<<"ra = "<<endl;
return 0;
}
运行结果:
出现这种情况是因为 源程序 形成 可执行程序时需要经历4个步骤,分别是 预编译 , 编译 ,汇编, 链接 ;
其中 预编译阶段
a) 删除所有的"#define",并且展开所有的宏定义;所有的const内置类型在预编译阶段就已经做了类似于宏替换的工作
b) 处理所有的条件预编译指令,"#if"、"#ifdef"、"#endif"等;
c) 处理"#include"预编译指令,将被包含的文件插入到该预编译指令的位置;
d) 删除所有的注释;
e) 添加行号和文件名标识,以便于编译器产生调试用的符号信息及编译时产生编译错 误和警告时显示行号;
f) 保留所有的#pragma 编译器指令,因为编译器需要使用它们。
2.自定义类型
c++
class Int
{
int value;
public:
Int(int x = 0) : value(x) {}
~Int() {}
void SetValue(int x) { value = x; }
int GetValue() const { return value; }
};
int main()
{
const Int a(10);
cout <<"a: " << a.GetValue() << endl;
Int *ip = const_cast<Int *>(&a);
Int &b = const_cast<Int &>(a);
ip->SetValue(100);
cout <<"a : " << a.GetValue() << endl;
b.SetValue(200);
cout <<"a : " << a.GetValue() << endl;
system("pause");
}
运行结果:
结论:
- 对于自定义类型常量,不会进行类似宏替换操作。
- 常量指针被转化成非常量的指针,并且仍然指向原来的对象;
- 常量引用被转换成非常量的引用,并且仍然引用原来的对象;
3. 重新解释转换 reinterpret_cast
用法:reinpreter_cast<type_name> (val)
说明:
type_name必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换
成一个整数,也可以把一个整数转换成一个指针。类似C语言的强转。
用途:
1.用于指针类型间的强制转换 (无法去除const)
- 基本类型
c++
int main()
{
int a = 0x61626364; // a b c d
char* p =reinterpret_cast<char*>(&a);
cout << *p << endl;
p += 1;
cout << *p << endl;
p += 1;
cout << *p << endl;
p += 1;
cout << *p << endl;
}
内存分布图:
运行结果:
- 自定义类型
c++
class Object
{
private:
int value;
public:
Object(int x = 0) :value(x) {}
~Object() {}
int& Value() { return value; }
const int& Value() const { return value; }
};
int main()
{
Object obj(10);
int* p = reinterpret_cast<int*>(&obj);
int& a = reinterpret_cast<int&>(obj);
cout << obj.Value() << endl;
*p = 100;
cout << obj.Value() << endl;
a += 100;
cout << obj.Value() << endl;
return 0;
}
运行结果:
注:对于自定义的类型尽量不要进行此类转换,如虚基类(含有虚基类指针),有函数虚函数的类(含有虚表指针),它们的地址空间中的成员信息分布计算容易出错,导致错误访问。
2.用于整数和指针类型间的强制转换
c++
void fun(void* p)
{
int a = reinterpret_cast<int>(p); //指针到整数
cout<<a<<endl;
}
int main()
{
int a = 100;
fun(reinterpret_cast<void*>(a)); //整数到指针
return 0;
}
运行结果:
4.动态转换 dynamic_cast
原理:RTTI
博客链接:点击跳转(建议先了解)
用法: dynamic_cast<type_name> (val)
说明:必须是公有继承,基类要有虚函数。
允许在运行时刻进行类型转换 ,从而使程序能够在一个类层次结构中安全地转换类型, 把基类指针转换成派生类指针,或把指向基类的左值转换成派生类的引用。
type_name 只能是自定义类型的指针或引用。
特点:
- 与C++支持的其他强制转换不同的是, dynamic_cast 是在运行时执行的类型转换。
- 如果针对指针类型的 dynamic_cast 失败, 则dynamic_cast 的结果是 nullptr。
- 如果针对引用类型的 dynamic_cast 失败, 则 dynamic_cast 会抛出一个异常。
- 在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。
- 在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
我们主要来看下行转换
c++
class Object
{
private:
int value;
public:
Object(int x = 0) :value(x) {}
virtual void func() { cout << "value: " << value << endl; }
};
class Base : public Object
{
private:
int num;
public:
Base(int x = 0) :Object(x + 10), num(x) {}
void func() { cout << "num: " << num << endl; }
};
1.正确的下行转换
c++
int main()
{
Base base;
Object* pobj = &base;
Base* pa = dynamic_cast<Base*>(pobj); //动态转换
}
图解:
运行时检查
2.错误的下行转换
c++
int main()
{
Base base;
Object* pobj = nullptr;
Object obj;
pobj = &obj;
Base* pa = dynamic_cast<Base*>(pobj); // 动态转换 pa = nullptr
if(pa == nullptr) {cout << "hello world!" << endl;}
Base* pb = static_cast<Base*>(pobj); //不安全
}
图解:
运行时检查
运行结果:
3.引用
c++
int main()
{
Base base(22);
Object obja(11);
Object& obj = obja;
try
{
Base& a = dynamic_cast<Base&>(obj); // 动态转换
a.func();
}
catch (std::bad_cast& e)
{
cout << e.what()<<endl;
}
Object& obj1 = base;
Base& b = dynamic_cast<Base&>(obj1); // 动态转换
b.func();
}
图解:
运行结果:
结论:
使用异常给程序增加了相应的运行开销 ,所以dynamic_cast尽可能使用指针。
- 和void型指针结合问题
c++
int main()
{
Base b;
Object& obj = b;
void* pobj = &obj; //正确,void型指针可以指向任意类型
Base* pb = dynamic_cast<Base*> (pobj); //error,pobj的类型是void(无类型信息)
}
结论:
dynamic_cast 的操作数必须是指向完整类型的指针。