参考:
https://blog.youkuaiyun.com/shidantong/article/details/80864149 (写的超棒!没有写一大堆让人头疼的东西)
C++为了规范C中的类型转换,引入了四种强制类型转换操作;
1.static_cast(编译时类型检查)
相当于C中的隐式类型转换,c++中的隐式类型转换就是使用static_cast。不能用于两个不相关类型的转换
/*基本类型转换*/
void Test(){
int i=10;
double d1=i;//隐式类型转换
double d2=static_cast<double>(i);
}
有一种值得注意的情况: static_cast也可以用于类层次结构中父类和子类之间(自定义类型)指针和引用的转换。不一定包含虚函数。这种转换方式是不安全的。
class A{
public:
A(int a):_a(a){}//这里涉及到C++11的一些构造函数
private:
int _a;
};
void Test(){
A a1(10);
A a2=20;//OK 隐式转换,等价于 A a2=A(20);
A a3=static_cast<A>(30);
}
因此,单参的构造函数会进行隐式类型转换,C++中即可用static_cast来转。但是这种隐式类型转换有时候是不安全的,尤其是在使用智能指针时,当我们将一个不需要用户自己管理释放空间的指针交给智能指针时,系统并不会报错,但是智能能指针使用完会调用析构函数来释放空间,此时就会出问题。
为了防止这种单参构造函数的隐式类型转换,C++提供了一个关键字 explicit来消除这种隐式转换。
2.dynamic_cast(运行时类型检查)
保证下行转换不会出错(这里又牵扯到内存问题 )只能接受引用,指针,内置对象。
/*强制类型转换*/
void Test(){
int i=10;
double d1=i;//隐式类型转换
double d2=static_cast<double>(i);
}
/*父类与子类之间的转换*/
class Base{
public:
virtual void f(){
cout<<"base f()"<<endl;
}
private:
int _a1;
};
class Derived:public Base{
public:
virtual void f() override{
cout<<"derived f()"<<endl;
}
private:
int _a2;
};
void Test(){
Base* b=new Derived();
Derived* d1=static_cast<Derived*>(b);
Derived* d1=dynamic_cast<Derived*>(b);
/*上面都能成功*/
Base* b=new Base();
Derived* d1=static_cast<Derived*>(b);
Derived* d1=dynamic_cast<Derived*>(b);//失败
}
对于上行转换,dynamic_cast和static_cast是一样的。
注意:
1.dynamic_cast只能用于含有虚函数的类;由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。
2.dynamic_cast在转换时会先检查能否转换成功,能就进行转换,不能就返回0
C++11 override的作用
作用:
- 在函数比较多的情况下可以提示读者某个函数重写了基类虚函数(表示这个虚函数是从基类继承,不是派生类自己定义的);
- 强制编译器检查某个函数是否重写基类虚函数,如果没有则报错。
简单来说就是override表示当前函数重写了基类的虚函数,(防止由于粗心本身打算重写却写错了变成别的函数)详见
3.reinterpret_cast
字面意思重新解释,相当于C中的强制类型转换,可将一种类型转换成另一种不相关类型
void Test(){
int i=10;
int* p1=(int*)i;//强制类型转换
int* p2=reinterpret_cast<int*>(i);
}
4.const_cast
作用:
- 去除指针或引用的const属性。
- 将常量对象强转为非常量对象
- 修改类型的volatile属性!??
简单的说一下const能够放在两个位置,放在变量类型左边表示该变量是const,放在右边说明该指针是const不能被修改
void Test(){
int i = 10;
const int* p1 =&i;//去除指针的const
int* p2=const_cast<int*>(p1);
//*p1=20;//error
*p2=20;//正确,p1,p2,i的值相同,地址相同
int a = 2;
const int &c_a = a;//去除引用的const
//c_a = 5;//错误,常量不能改变
int &a2 = const_cast<int&>(c_a);
a2 = 5;//正确,a,a2,c_a的值均为5,地址相同
}
但是!!!!
void Test(){
const int i=10;
int* p=const_cast<int*>(&i);
int& ref_i=const_cast<int&>(i);
*p=20;
cout<<i<<endl;//10
cout<<*p<<endl;//20
cout<<ref_i<<endl;//20
}
i的值可以修改,但p,&i,&ref_i明明地址相同!结果却不一样。
关键字volatile
这是因为一旦将一个变量定义成const类型,编译器会进行优化,将该值放在寄存器中,默认他是不能被改变的,每次在使用时直接从寄存器中取数据,即使我们改变了他在内存中的值。
我们可以借助一个关键字volatile,保证每次从内存中读取数据。和 const 修饰词类似,const 有常量指针和指针常量的说法,volatile 也有相应的概念:
修饰由指针指向的对象、数据是 const 或 volatile 的:
const char* cpch;
volatile char* vpch;
修饰指针自身
char* const pchc;
char* volatile pchv;
因此可以:
volatile const int i = 10;//可以都输出20,
Ps. 为找工作做准备,尽量关联面经中知识点