本期,我将给大家带来的是关于C++中的类型转换,有关这部分的知识是一个非常重要的概念,在实际编程中可能需要将一种类型转换为另一种类型,以便进行后续的操作!!!
目录
在学习C++的类型转换之前,我们先来回顾下有关C语言中类型转换的相关知识!!!
发生的时机:?
而C语言中总共有两种形式的类型转换:
?接下来,我分别对上述两种类型转换进行代码层面的解释说明:
隐式类型转换:?这种转换是由编译器根据上下文进行的:
解释说明:
Test()
函数中,定义了一个整型变量a
并赋值为1。然后,将整型变量a
隐式地转换为浮点型变量b
,这是因为在表达式中涉及到不同类型的操作数时,编译器会自动进行隐式类型转换。a
被隐式转换为浮点型变量b
,所以b
的值为1.00。💨💨但是此时如果你想将一个`double`类型的变量转换为`int`,由于计算机中整数存储方式的不同,可能会丢失一些小数部分的信息,这就会造成精度损失:
解释说明:
double
类型的浮点数,但将其隐式转换为int
类型时,小数部分将被截断,导致精度损失。?💨💨 除了上述情况之外,可能还会碰到以下情况。例如以下示例:
?解释说明:
💨💨 隐式类型转换可能导致一些意外的行为,特别是在混合使用不同类型的操作时:
?解释说明:
【小结】
显式类型转换:程序员明确地使用类型转换运算符进行类型转换。这有助于确保程序员了解可能发生的数据丢失,并提供更精细的控制。
??解释说明:
num1 + num2
的结果强制转换为整数;num1 + num2
的结果是 13.14
,但由于强制转换为整数,最终的 result
变量的值为 13
。💨💨 其次可能还有遇到将指针的值转换为整数类型的情况:
??解释说明:
p
转换为整数类型,并将结果赋值给整型变量address;C语言还提供了一些类型转换的库函数,例如atoi()、atof()等,用于将字符串转换为整数、浮点数等。这些函数执行严格的转换,并提供错误处理机制。
【小结】
当我们像下面这样把一个算术类型的值赋给另一种类型时:
bool a = 10; //a为真
int i = a; //i的值为1
i = 3.1415; //i的值为3
double pi = i; //pi的值为3.0
unsigned char b = -1; //假设char占8比特,则b为255
signed char c = 256; //假设char占8比特,c的值是未定义的
?类型所能表示的值的范围决定了转换的过程:
🔥?🔥?含有无符号类型的表达式
💨💨尽管我们不会故意给无符号对象赋--个负值,但是保不齐大家会写出这么做的代码。例如,当一个算术表达式中既有无符号数又有int值时,那个int值就会转换成无符号数。把int转换成无符号数的过程和把int直接赋给无符号变量一样:
?【解释说明】
💨💨当从无符号数中减去一个值时,不管这个值是不是无符号数,我们都必须确保结果不能是一个负数:
在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针:
int arr[10]; //含有10个整数的数组
int* num = arr; //arr转换成指向数组首元素的指针
指针的转换: C++还规定 了几种其他的指针转换方式:
使用整数值 0
:
//整数值 0 可以隐式转换为指针类型,因此 arry 被初始化为空指针。
int* arry= 0; // 或 int* arry= NULL;
?使用字面值 nullptr
:
//nullptr 是C++11引入的空指针字面值,它可以被隐式转换为任意指针类型
//相较于使用整数值 0,使用 nullptr 更为类型安全,因为它是一个特殊的空指针值,而不是整数。
int* arry= nullptr;
👉 这种转换的目的是为了提供一种通用的指针类型,可以指向任何非常量的数据?👈
【解释说名】:
void*;
void*
是一种通用的指针类型,可以指向任何非常量的数据;void*
会失去指针指向对象的具体类型信息,因此在使用时需要谨慎。?1??整数类型到bool
2??指针类型到bool
3??其他类型到bool
int i;
const int& j = i; //非常量转换成const int的引用
const int* p = &i;//非常量的地址转换成 const的地址
👉 但是需要注意一点的是不允许将const 转换为非常量,编译器会发生报错 👈
?
标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
C风格的转换格式很简单,但是有不少缺点的:
- 1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
- 2. 显式类型转换将所有情况混合在一起,代码不够清晰
因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。
有时我们希望显式地将对象强制转换成另外一种类型。例如,如果想在下面的代码中执行浮点数除法:
int i,j;
double res = i/j;
接下来我将对每一种进行简要的说明:
💨💨接下来介绍static_cast
的几个常见用法和特点:
示例如下:
示例如下:
示例如下:
class Arry{
public:
virtual void foo() { cout << "Arry::foo()" << endl; }
};
class Num: public Arry{
public:
virtual void foo() { cout << "Num::foo()" << endl; }
};
Arry* pBase = new Num();
// 创建一个派生类对象,并将其赋值给基类指针
Num* pDerived = static_cast<Num*>(pBase);
// 将基类指针pBase转换为派生类指针pDerived
示例如下:
【注意】
💨💨举个例子,现在有以下类型的转换:
int *num;
char *pc = reinterpret_ cast<char*>(num) ;
我们必须牢记pc所指的真实对象是一一个int而非字符,如果把pc当成普通的字符指针使用就可能在运行时发生错误。例如:
string str(pc) ;
注意事项:
- 使用reinterpret_ _cast 是非常危险的,用pc初始化str的例子很好地证明了这一.点。其中的关键问题是类型改变了,但编译器没有给出任何警告或者错误的提示信息。
- 当我们用一个int的地址初始化pc时,由于显式地声称这种转换合法,所以编译器不会发出任何警告或错误信息。
- 接下来再使用pc时就会认定它的值是char*类型,编译器没法知道它实际存放的是指向int的指针。最终的结果就是,在上面的例子中虽然用pc初始化str没什么实际意义,甚至还可能引发更糟糕的后果,但仅从语法上而言这种操作无可指摘。
💨💨再给大家验证reinterpret_cast是将一种类型转换为另一种不同的类型:
?【解释说明】
static_cast
在进行指针类型转换时,只能用于具有继承关系的类层次结构之间的指针类型转换,或者用于指针和整数类型之间的转换。但它不能用于两个不相关的类型进行转换const
或 volatile
修饰💨💨?以下是const_cast
的常见应用场景:
const
修饰:?示例如下:
【解释说明】
而在vs下,我通过调试可以发现这里发生的是替换:
此时,当我们可以加上 volatile 关键字之后,则可以防止这种优化操作:
?此外,上述操作我们还可以使用 C语言的强制类型转换机制.具体如下:
?
dynamic_cast:用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)。它主要用于类层次结构中的向上转换和向下转换,并且只能用于包含虚函数的类。
💨💨?dynamic_cast
的主要特点和应用场景如下:
dynamic_cast
将其转换为派生类指针或引用。向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的),而对象则不可以
?而对于安全或者不安全这个话题我们可以通过代码来进行理解:
class A
{
public :
virtual void f(){}
};
class B : public A
{};
void fun (A* pa)
{
// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
B* pb1 = (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;
}
【解释说明】
因为大家看(A *pa)这是一个父类的指针,对于上转下,一般情况我们是没有上转下的需求的,只有一种情况有上转下的需求。此时有没有这样一种可能,这是一个父类的指针,这个父类的指针如果本身就是指向父类,你给他转回子类可不可以呢?可以,但是有风险存在。
?强制类型转换或者static这种转换,它都是很暴力的,它不管你指父类或者指向子类,都给你转成功:
而dynamic_cast在这个地方的好处就是:它是先检查你这个父类的指针,如果你指向父类,此时就转换失败,如果你指向子类呢,我才会转换成功:
注意
以上便是本文的主要内容了。接下来,简单总结一下本文内容!!!
隐式类型转换:当编译器自动将一种类型转换为另一种类型时,称为隐式类型转换。例如,将一个整数赋值给一个浮点数变量时,编译器会自动将整数转换为浮点数。
显式类型转换:当程序员明确指定将一种类型转换为另一种类型时,称为显式类型转换。在C++中,有四种显式类型转换运算符:static_cast、dynamic_cast、const_cast和reinterpret_cast。
static_cast:用于基本的类型转换,如将整数转换为浮点数,指针转换为整数等。它可以在编译期进行类型检查,但不能用于没有继承关系的类之间的转换。
dynamic_cast:用于在继承关系中的类型转换。它会在运行时进行类型检查,如果转换不安全,则返回nullptr或抛出异常。
const_cast:用于去除变量的const或volatile属性。它可以用于修改常量对象的值,但需要谨慎使用,因为违反了const的语义。
reinterpret_cast:用于将一个指针或引用转换为另一个不相关的类型。它没有类型检查,因此非常危险,可能导致未定义的行为。
总的来说,类型转换是将一个类型的值转换为另一个类型的过程。在C++中,可以通过隐式类型转换和显式类型转换来实现不同类型之间的转换。在进行类型转换时,需要注意类型的兼容性和安全性,避免出现错误或未定义的行为。
本文的内容讲解便到此结束,感谢大家的观看与支持!!!
?