《C++文章汇总》
上一篇介绍了《014-模板、自定义动态数组》,本文介绍类型转换。
1.类型转换
◼ C语言风格的类型转换符
(type)expression
type(expression)
int main(){
int a = 10;
double d = static_cast(a);
//隐式转换
// double d = a;
//显式转换
// double d = (double) a;
// double e = double(a);
getchar();
return 0;
}
◼ C++中有4个类型转换符
static_cast
dynamic_cast
reinterpret_cast
const_cast
使用格式:xx_cast(expression)
I.const_cast
◼ 一般用于去除const属性,将const转换成非const
两种写法,汇编代码一样
const Person *p1 = new Person();
Person *p2 = const_cast(p1);
Person *p3 = (Person *)p1;
II.dynamic_cast
◼ 一般用于多态类型的转换,有运行时安全检测
明显看到不安全的指针指向p1赋值给stu1没有赋值成功,stu1为0x0空指针NULL,dynamic_cast进行了安全检测,p2则成功赋值给了stu2
class Person{
int m_age;
virtual void run(){};
};
class Student:public Person{
};
int main(){
Person *p1 = new Person();
Person *p2 = new Student();
cout << "p1 = " << p1 << endl;
cout << "p2 = " << p2 << endl;
//原始做法
// Student *stu1 = (Student *)p1;
// Student *stu2 = (Student *)p2;
Student *stu1 = dynamic_cast(p1);//不安全,子类指针指向父类对象
Student *stu2 = dynamic_cast(p2);//安全
cout << "stu1=" << stu1 << endl;
cout << "stu1=" << stu2 << endl;
}
//输出
p1 = 0x100658690
p2 = 0x100659bb0
stu1=0x0
stu1=0x100659bb0
原始赋值方式不会做安全检测,直接赋值过去,并不会将指针置为0x00000000(NULL)
//原始做法
// Student *stu1 = (Student *)p1;
// Student *stu2 = (Student *)p2;
Car对象和Person对象一点联系也没有,赋值会做安全检测置为NULL
class Car{
int m_age;
int m_price;
int m_height;
};
int main(){
Person *p1 = new Person();
Person *p2 = new Student();
cout << "p1 = " << p1 << endl;
cout << "p2 = " << p2 << endl;
Car *c1 = (Car *)p1;
Car *c2 = dynamic_cast(p2);
cout << "c1 = " << c1 << endl;
cout << "c2 = " << c2 << endl;
}
//输出
p1 = 0x1005aa3d0
p2 = 0x1005a8980
c1 = 0x1005aa3d0
c2 = 0x0
III.static_cast
◼ 对比dynamic_cast,缺乏运行时安全检测
◼ 不能交叉转换(不是同一继承体系的,无法转换)
◼ 常用于基本数据类型的转换、非const转成const
int a = 10;
double b = static_cast(a);
Person *p1 = new Person();
const Person *p2 = static_cast(p1);//相当于const Person *p2 = p1;不写转换也会自动转
◼ 使用范围较广,大多数转换都能适用
IV.reinterpret_cast
◼ 属于比较底层的强制转换,没有任何类型检查和格式转换,仅仅是简单的二进制数据拷贝
◼ 可以交叉转换
◼ 可以将指针和整数互相转换
int main(){
//小端模式0A 00 00 00/0000 1010 0000 0000 0000 0000 0000 0000
int a = 10;
//0A 00 00 00 00 00 00 00理想情况
//00 00 00 00 00 00 24 40实际情况,浮点数据的存储和整形数据的存储是不一样的,寄存器的使用不同
double d = a;
//理想情况下汇编代码
//mov eax,dword ptr [a]
//mov dword ptr [d], eax
/**实际情况
汇编代码如下,并不是简简单单的二进制拷贝
xmm0,mmword ptr [__real@4024000000000000 (0979CA0h)]
mmword ptr [d],xmm0
*/
cout << "a = " << a << endl;
cout << "d = " << d << endl;
}
//输出
a = 10
d = 10
仅仅只单独的进行二进制拷贝,a的值拷贝到d中,则使用reinterpret_cast,最后得出的值d就不确定了d = 4.94066e-323
int main(){
int a = 10;
double d = reinterpret_cast(a);
//汇编代码如下
/**
movsd xmm0,mmword ptr [a]
movsd mmword ptr [d],xmm0
二进制拷贝完成后d中内存空间为
0A 00 00 00 cc cc cc cc
*/
cout << "a = " << a << endl;
cout << "d = " << d << endl;
}
//输出
a = 10
d = 4.94066e-323
小数的存储格式,8.25-->1000.01-->1.00001*2^3(3是指数,00001是尾数)
int *p = reinterpret_cast(0x100);
int b = reinterpret_cast(p);
int数据拷贝给double类型要写引用,int *直接给int不用写引用
int *p = reinterpret_cast(0x100);
int b = reinterpret_cast(p);
//无需写成引用
int a = 10;
double d = reinterpret_cast(a);
2.C++11新特性
◼ auto
可以从初始化表达式中推断出变量的类型,大大简化编程工作
class Person{
public:
void run(){
};
};
int main(){
auto a = 10;//int
auto str = "c++";//const char *
auto p = new Person();//Person *
p->run();
}
属于编译器特性,不影响最终的机器码质量,不影响运行效率
◼ decltype
可以获取变量的类型
int b = 10;
decltype(b) c = 20;//int
◼ nullptr
可以解决NULL的二义性问题
c++98,NULL宏定义为0,产生了二义性,两个函数都满足调用条件
void func(int v){
cout << "void func(int v)" << v << endl;
}
void func(int *v){
cout << "void func(int *v)" << v << endl;
}
int main(){
int m = NULL;
int *n = NULL;
}
c++11开始NULL为整数0,空指针用nullptr
void func(int v){
cout << "void func(int v)" << v << endl;
}
void func(int *v){
cout << "void func(int *v)" << v << endl;
}
int main(){
int m = NULL;
int *n = nullptr;
int *q = new int;
delete q;
q = nullptr;
func(NULL);
func(nullptr);
}
//输出
void func(int v) - 0
void func(int *v) - 00000000
◼ 快速遍历
int array[] = {10,20,30,40,50};
for(int item:array){
cout << item << endl;
}
//输出
10
20
30
40
50
更加简洁的初始化方式
int array0[] {10,20,30,40,50};//等价于int array[] = {10,20,30,40,50};