C++类型转换

C++的类型转换

1.C语言中的类型转换

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化。

C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换。

  1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
  2. 显式类型转化:需要用户自己处理
int main()
{
    int i = 1;
    double d = 8.88;
    i = d;//C语言支持相近类型的隐式类型转换(相近类型,也就是意义相似的类型)
    cout<<i<<endl;
    
    int * p=nullptr;
    p = (int* i);//c语言支持相近类型的强制类型转换。(不相近类型,也就是意义差别很大的类型)
    cout<< p <<endl;
}

缺陷:转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换。

2.为什么C++需要四种类型转换

C风格的转换格式很简单,但是有不少缺点的:

  1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
  2. 显式类型转换将所有情况混合在一起,代码不够清晰

因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言留下的隐式转换和显示转换,所以C++中还可以使用C语言的转化风格。

进行C++的强制类型转换就很清晰地感受到发生了什么,以及对可能危险的const_cast有察觉。

3.C++强制类型转换

3.1static_cast

static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。

对应于原来C语言的隐式类型转换。

int main()
{
    int i = 1;
    double d = 8.88;
    //对应C语言的隐式类型转换
    d = static_cast<double>(i);
}
int main()
{
    int i = 1;
    double d = 8.88;
    i = d;//C语言支持相近类型的隐式类型转换(相近类型,也就是意义相似的类型)
}

3.2reinterpret_cast

reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型。

对应于原来C语言的强制类型转换。

int main()
{
    int i = 1;
    double d = 8.88;
    
    int * p=nullptr;
    p = (int* i);//c语言支持相近类型的强制类型转换。(不相近类型,也就是意义差别很大的类型)
    cout<< p <<endl;
}
int main()
{
    int i = 1;
    double d = 8.88;
    //对应C语言的强制类型转化
    p = reinterpret_cast<int*>(i);
}

3.3const_cast

const_cast最常用的用途就是删除变量的const属性,方便赋值。

int main()
{
    const int ci = 10;
    const int *p = &ci;
    /*对应c语言强制类型转换中去掉const属性的(不相近类型)*/
    int* pi = (int*)(&ci);
    *pi = 20;
    cout<<*pi<<endl;//20
    cout<<ci<<endl;//10
}

这里的20和10是一个常见笔试题。

因为const类型是不会更改的,会被编译器放到寄存器里。这里能被改是因为强转。

这里打印10是因为ci存储的内存被改了,但是ci被放进了寄存器,这里去寄存器中取,还是10。本质这是由于编译器对const对象存取优化机制所致。

也就是之前在linux中提及的volatile关键字的作用。

int main()
{
    const int ci = 10;
    const int *p = &ci;
    /*对应c语言强制类型转换中去掉const属性的(不相近类型)*/
    int* pi = const_cast<int*>(&ci);
    *pi = 20;
    cout<<*pi<<endl;//20
    cout<<ci<<endl;//10
}

反汇编情况下是可以看到的,不过不同汇编处理机制不同。

int main()
{
    volatile const int ci = 10;
    const int *p = &ci;
    /*对应c语言强制类型转换中去掉const属性的(不相近类型)*/
    int* pi = (int*)(&ci);
    *pi = 20;
    cout<<*pi<<endl;//20
    cout<<ci<<endl;//10
}

3.4dynamic_cast

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)

向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)

向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)

使用场景:判断当前父类指针实际指向的父类对象还是子类对象。

注意:

  1. dynamic_cast向下转换只能针对继承中的多态类型(父类必须包含虚函数)
    1. dynamic_cast原理:dynamic_cast通过去虚表中的上方存储的标识信息,来判断指向父类对象还是子类对象
    2. image-20220217220416737
  2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0。
class A
{
    public:
    	virtual void f(){}
    protected:
    	int _a;
};
class B: public A
{
    protected:
    	int _b;
};
int main()
{
     A a;
     B b;
    
    //C++中子类对象可以赋值给父类对象、指针、引用。这个过程中会发生切片。
    //语法天然支持的,这个叫向上转换
    A* pa = &a;
    pa = &b;  
}

向下转换使用C语言的强制类型转换是无法做到的。

class A
{
    public:
    	virtual void f(){}
    protected:
    	int _a;
};
class B: public A
{
    protected:
    	int _b;
};
void f_cast(A* pa)
{
    //如果想区分pa是指向父类对象还是子类对象
    B* pb = (B*)pa;
    pb->_a = 1;
    pb->_b = 2;
}
int main()
{
    A a;
    B b;
	//如果是父类的指针或者引用,传给子类的指针,这个过程叫向下转换,这个过程有可能能成功,要看具体情况    
	A* pa =&a;
    f_cast(pa);//会访问非法内存就报错
    
    pa = &b;
    f_cast(pa);
}

如果父类指针被强转就会访问非法内存,被页表禁止。

image-20220217214935407

使用c++提供的dynamic_cast是可以做到的。

class A
{
    public:
    	virtual void f(){}
    protected:
    	int _a;
};
class B: public A
{
    protected:
    	int _b;
};
void f_cast(A* pa)
{
    //如果想区分pa是指向父类对象还是子类对象
    B* pb = dynamic_cast<B*>(pa);
    /*如果pa是指向子类对象,则转换成功*/
    /*如果pa指向父类对象则转化失败返回nullptr*/
    if( pb != nullptr)
    {
        cout<<"指向子类对象"<<endl;
        pb->_a = 1;
        pb->_b =2;
    }
    else{
        cout<<<"转换失败,pa指向父类对象"<<endl;
    }
}
int main()
{
    A a;
    B b;
	//如果是父类的指针或者引用,传给子类的指针,这个过程叫向下转换,这个过程有可能能成功,要看具体情况    
	A* pa =&a;
    f_cast(pa);//可能报错
    
    pa = &b;
    f_cast(pa);
}

注意强制类型转换关闭或挂起了正常的类型检查,每次使用强制类型转换前,程序员应该仔细考虑是否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用域,以减少发生错误的机会。

如果需要强制类型转换最好按规范使用。

3.5explicit

explicit关键字阻止经过转换构造函数进行的隐式转换的发生

class A
{
  	public:
    	A(int a)
        {
            cout<<"A(int a)"<<endl;
        }
    	A(int a1,int a2)
        {
            cout<<"A(int a1,int a2)"<<endl;
        }
    	A(const A& a)
        {
            cout<<"A(const A& a)"<<endl;
        }
    private:
    	int _a;
};
int main()
{
     A a1(1);
    // 隐式转换-> A tmp(1); A a2(tmp);
     A a2 = 1;
    /*创建临时对象,再进行拷贝;连续的两个构造优化程直接构造*/
     A aa3 = {1,2};//C++11初始化列表
}
class A
{
  	public:
    	explicit A(int a)
        {
            cout<<"A(int a)"<<endl;
        }
    	explicit A(int a1,int a2)
        {
            cout<<"A(int a1,int a2)"<<endl;
        }
    	A(const A& a)
        {
            cout<<"A(const A& a)"<<endl;
        }
    private:
    	int _a;
};
int main()
{
     A a1(1);
    // 隐式转换-> A tmp(1); A a2(tmp);
     A a2 = 1;
    /*创建临时对象,再进行拷贝;连续的两个构造优化程直接构造*/
}

4.RTTI(了解)

RTTI:Run-time Type identification的简称,即:运行时类型识别。

C++通过以下方式来支持RTTI:

  1. typeid运算符
  2. dynamic_cast运算符
  3. decltype(c++11中识别对象类型再使用对象类型定义)

5.常见面试题

  1. C++中的4中类型转化分别是:_________、_________、_________、_________
  2. 说说4中类型转化的应用场景。
    1. static_cast——相近类型的隐式类型转换
    2. reinterpret_cast——不相关类型的强转类型转换
    3. const_cast——不相关的类型去掉const属性
    4. dynamic_cast——多态类型的向下类型转换,父类指针指向父类转换失败。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值