c++学习笔记(6)-类型转换

2023-12-26 19:44:38

1、概念

C++类型转换是将一种数据类型转换为另一种数据类型的过程。

2、分类

C++中的类型转换可以从3个角度来划分:

  • 根据类型转换是由程序员显式指定,还是由编译器自动完成,分为显式类型转换和隐式类型转换;
  • 根据参与类型转换的变量类型,分为标准类型转换和用户定义类型转换;
  • 根据类型转换的运算规则,分为按照存储值转换和按照数值转换。

2.1、显式和隐式类型转换

image.png
下面是显式类型转换和隐式类型转换的例子:
1、显式类型转换,在下面的代码中,变量b是double类型,使用显式类型转换将其转换为int类型,并将结果赋值给变量c。

 // 将double类型的变量b转换为int类型
double b = 3.14; 
int c = (int)b;

2、隐式类型转换,在下面的代码中,变量a是int类型,变量b是double类型,当它们参与除法运算时,系统会自动将变量a转换成double类型,使得运算结果也是double类型。这就是隐式类型转换。

// 变量a会被隐式转换为double类型,然后进行除法运算
int a = 5;
double b = 3.14; 
double c = a / b; 

2.2、标准类型转换和用户定义类型转换

标准类型转换指C++ 基本类型之间的转换,可以概括为以下几类:

  • 数值、字符、bool、枚举类型之间的转换;
  • 指针类型之间、引用类型之间的转换;
  • 限定符的添加和删除:比如const,volatile。

用户定义类型转换指类类型与基本类型、或者类类型之间的转换。用户定义类型转换,需要程序员实现以下两种成员函数:

  • 转换构造函数:定义在类中的一种特殊的构造函数,它可以将其它类型转换为该类的一个对象。
  • 类类型转换操作符:定义在类中的一种特殊的成员函数,它可以将一个对象转换为另一个基本类型。

举个例子:

//重载int运算符,把Object转化为int类型
class Object{
public:
    //转换构造函数
    Object(int num)m_number(num){}
    //类类型转换操作符:定义int运算符
    operator int(){return number;}
private:
    int m_number;
};

int main() {
    int number = 2;
    Object obj = number;//把int类型转换为Object类型
    int newNumber = (int)obj;//把Object类型转换为int类型
    return 0;
}

2.3、按照存储值转换和按照数值转换

按照存储值转换通常是指在某些情况下进行数据类型的强制转换,这种转换是按照字符在计算机中存放的二进制值进行转换的,包括:int、short、char、bool、void、指针类型、signed和unsigned类型之间的转换。
按照数值转换通常是针对数值类型进行的,这种转换是按照数值大小进行等值转换,包括:浮点数类型和非浮点数类型之间的转换,比如double和int之间的转换。

3、指针类型转换

C++提供了4种运算符,用于指针(引用)类型转换,4种运算符号的区别如下:
image.png
虽然说上述运算符号还可用于把指针类型转换为非指针类型,但应用场景少,不考虑。

3.1、reinterpret_cast

reinterpret_cast用于任意指针(引用)类型之间的转换。转换运算过程中,只是简单地把源变量的二进制值拷贝到新的类型变量中,不作任何类型检查。例如,对于以下代码:

int main() {
    int* p1 = new int(10);
    char* p2 = reinterpret_cast<char*>(p1);
    std::cout << *p1 << std::endl;  // 输出10
    std::cout << *p2 << std::endl;  // 输出未定义的值
    return 0;
}

在这个例子中,将一个int类型的指针p1转换为了char类型的指针p2,虽然可以取得p2所指向的地址,但是由于reinterpret_cast忽略了指针类型所指向的对象类型,因此输出的值是未定义的。因此,在使用reinterpret_cast时,需要非常小心和谨慎。

3.2、static_cast

static_cast用于子类指针(引用)和父类指针(引用)之间的相互转换,在编译期执行类型检查。在C++中,使用static_cast将基类指针转换为子类指针是一种常见的操作,但是在实际使用时,可能会出现以下错误,假设我们有如下的类层次结构:

class Base {
public:
    virtual ~Base() {}
};

class Derived : public Base {
public:
    int data;
};

我们创建一个Base类的指针,并将其指向基类对象:

Base* basePtr = new Base;

此时,如果试图使用static_cast将basePtr指针转换为Derived指针类型,会导致编译时不会发现错误,但是在运行时则会产生未定义行为,例如:

Derived* derivedPtr = static_cast<Derived*>(basePtr);
int data = derivedPtr->data;

这是因为类型转换时,将一个指向基类对象的指针转换为子类指针,但由于子类对象不存在,所以在使用子指针访问子类成员时会发生未定义行为。

3.3、dynamic_cast

dynamic_cast用于子类指针(引用)和父类指针(引用)之间的相互转换,在运行时进行类型检查。当用户试图使用dynamic_cast将基指针转换为子类指针类型,如果子类对象不存在,那么会返回nullptr。
使用dynamic_cast进行类型转换,并检查返回的指针是否为nullptr,就可以解决上述例子中static_cast引起的错误:

Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
if (derivedPtr) {
    int data = derivedPtr->data;
} else {
    std::cout << "转换失败" << std::endl;
}

3.4、const_cast

const_cast是C++中用于取消指针或引用对象的const限定符的一种操作符,它用于将const类型转换为非const类型。const_cast的学习难点在于理解它的使用场景,如果我们要把const类型转换为非const类型,为什么不一开始就把类型声明为const类型呢?这是因为const类型数据可能是第三方库定义的,当我们希望在不修改第三方库的源码的情况下,修改const数据,需要用到该运算符。

4、总结

类型转换在程序中具有重要的作用和意义,但在使用过程中需要注意类型转换的安全性,主要有以下两个方面:

  • 类型转换合法性。在进行类型转换时,需要保证转换的数据类型是合法的,例如不要把指针类型转换为字符类型,没有任何意义。
  • 精度损失和溢出问题。在进行类型转换时,需要考虑数据类型的精度,在某些情况下,类型转换可能会导致精度损失,例如将浮点数转换为整数时,小数部分会被截断,可能会导致精度丢失。另外,在一些情况下,类型转换也可能会导致数据溢出,例如将一个很大的整数转换为一个较小的整数类型,将导致数据溢出。

文章来源:https://blog.csdn.net/fanrongwoaini/article/details/135223249
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。