C++四种强制类型转换

四种强制类型转换


1. 出现的原因

C语言的强制类型转换,有着两种。

  1. 隐式类型转换
  2. 显示的强制类型转换

举例:

int main()
{
    int i = 1;
    double d = i;//隐式类型转换
    int* p = &i;
    int address = (int)p;//显示强制类型转换
    return 0;
}

缺陷:

  • 转换的可视化比较差,对于隐式类型转换来说
  • 对于所有的转化形式都是使用()类型书写的,却别程度不高

所以对于C++来说,为了加强类型转化的可视性,出现了四种强制类型转化

可以根据名字直接判断出是进行的什么类型的转化。

2. 四种类型转换分别是什么

1. static_cast

作用:用于非多态类型之间的转化(静态转化),等价于C语言中的隐式类型转化

缺点:不能用于两个不相关类型之间的转化。比如:int类型到int*类型的转化

举例:

int main()
{
    int i = 1;
    double d = static_cast<double>(i);
    std::cout << d << std::endl;
    retrun 0;
}

2. reinterpret_cast

作用:将一种类型转化为另外一种类型。等价于C语言中的显示强制类型转化

缺点:对于数据随意的转化,会出现无法预估的问题。尽量不要使用

举例:

int main()
{
    int i = 10;
    int* p = &i;
    int j = reinterpret_cast<int>(p);
    return 0;
}

3. const_cast

作用:删除变量的const属性,方便赋值

缺点:由于编译器的优化,就会导致访问const类型的变量的时候,不能实时的访问,有可能内存的值已经发生改变,但是访问到的还是旧的数据

解决:使用volatile关键字,确保内存的可见性,每次访问一个数据的时候到内存中直接去拿取数据

举例:

int main()
{
    const int i = 10;
    int* p = const_cast<int*>(&a);
    *p = 20;
    std::cout << a << std::endl;
    return 0;
}

4. dynamic_cast

作用:用于将一个父类对象的指针转化为一个子类对象的指针或者引用

缺点:当父类指针本来指向的是父类对象的时候,转化之后,使用子类类型的指针进行操作的时候,有可能造成内存访问越界

改善:只有当父类指针指向的就是子类对象的时候,在使用dynamic_cast强制类型转换

注意:

  1. dynamic_cast只能用于含有虚函数的类
  2. dynamic_cast会先检查是否能转换成功,能成功则转化,不能则返回0

对于父类指针指向的是父类对象会出错,返回0

举例:

class A
{
    public :
    virtual void f(){}
};

class B : public A
    {};

void fun (A* pa)
{
    // dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
    B* pb1 = static_cast<B*>(pa);
    B* pb2 = dynamic_cast<B*>(pa);
    cout<<"pb1:" <<pb1<< endl;
    cout<<"pb2:" <<pb2<< endl;
}

int main ()
{
    A a;
    B b;
    fun(&a);
    fun(&b);
    return 0;
}

3. 底层原理

底层原理有着两种:

  1. 数据是真正的发生改变了,在内存中新开辟了一个空间用于存储新数据,使用新数据的时候,不会影响到原有数据
  2. 数据没有发生改变,只是类型改变了,让编译器将这个类型的数据可以认为是另外一种类型的数据,只是数据合法了

static_cast是第一种类型:

根据上面的代码:

可以明显的看到对于两个数据,其地址不一样了,所以是开辟可一个新的内存空间,用来存放新的数据

reinterpret_cast是第一种类型:

根据上面的代码:

可以看到开辟新的内存空间了。

对于函数类型之间的转化也是开辟了一个新的函数,地址发生了改变。

举例:

using namespace std;
typedef void(*FUNC)();
int DoSomething(int i)
{
	cout << "DoSomething" << endl;
	return 0;
}
void Test()
{
	//
	// reinterpret_cast可以编译器以FUNC的定义方式去看待DoSomething函数
	// 所以非常的BUG,下面转换函数指针的代码是不可移植的,所以不建议这样用
	// C++不保证所有的函数指针都被一样的使用,所以这样用有时会产生不确定的结果
	//
	FUNC f = reinterpret_cast< FUNC>(DoSomething);
	f();
}

int main()
{
	Test();
	return 0;
}

了解:对于一个函数来说,一个函数也就是一个地址,当调用这个函数的时候,只需要函数名即可

可以看到两个的地址已经不一样了,所以发生了改变

const_cast是第二种类型:

注意一点:const_cast<>显示强制类型转换的时候,必须是指针,引用或者是指向对象类型成员的指针

进行强制类型转化之后,没有创建新的数据,没有开辟空间来存放指针,编译器只是将一种类型的指针当做了另外一种类型对的指针,确保了代码的合法性而已

根据上面的代码:

没有添加volatile:

添加volatile:

dynamic_cast是第二种类型

只是对于指针发生了改变,编译器对于解释的时候发生变化,没有开辟内存空间创建新的指针

根据上面的代码:

从该图可以看出,当父类指针指向的是父类对象的时候,进行dynamic_cast转化的时候,就会转化出错,返回0,所以第一次输出pb2的时候,其指针变量的值就是全0。

好的,我可以为您介绍一下关于C++中多态和虚函数的实验。 首先,多态是指不同对象以不同的方式响应相同的消息的能力。在C++中,通过虚函数来实现多态。虚函数是指在基类中被声明为虚函数的函数,在派生类中被重写后,在程序中使用基类指针或引用调用该函数时,会调用派生类中的函数。 下面是一个简单的多态和虚函数的实验示例: ```c++ #include<iostream> using namespace std; class Shape { public: virtual void draw() { cout << "Drawing Shape" << endl; } }; class Circle: public Shape { public: void draw() { cout << "Drawing Circle" << endl; } }; class Square: public Shape { public: void draw() { cout << "Drawing Square" << endl; } }; int main() { Shape *s; Circle c; Square sq; s = &c; s->draw(); s = &sq; s->draw(); return 0; } ``` 运行结果为: ``` Drawing Circle Drawing Square ``` 在上述示例中,我们定义了一个基类Shape和两个派生类Circle和Square,分别重写了基类的虚函数draw()。在程序中,我们先定义了一个基类指针s,然后将其指向第一个派生类对象c,再调用s的虚函数draw(),此时会调用派生类Circle的draw()函数,因为指针实际上指向的是Circle类型的对象。接着,我们将指针s重新指向第二个派生类对象sq,再次调用s的虚函数draw(),此时会调用派生类Square的draw()函数。 通过这个实验,我们可以看到多态和虚函数的特性,即通过基类指针或引用调用虚函数时,会根据实际对象的类型来调用相应的派生类函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TrustZone_Hcoco

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值