C++之类型转换

在C++或者大部分程序开发语言中,某些类型之间是有关联的,比如int和long,signed int和unsinged int等。这种相关联的类型之间是可以相互转换的。
一般这种转换可以分为两类:

  • 隐式转换
    自动执行的转换,程序中并没有显式指定类型转换。比如
    int ival = 3.14 + 3;
    
  • 显式转换
    也叫做强制类型转换。在代码中将某个类型强制转换成另一个相关类型。
    比如
    int i = 2;
    double d = static_cast<double>(i);
    

一、隐式转换

隐式类型转换是编译器自动进行的类型转换。下面是C++中一些重要的隐式类型转换规则:

1.1 算术类型转换

算术类型转换遵循一定的等级规则,一般
* 整型提升:较小的整型(如`char`、`short`)会被提升为`int`或`unsigned int`
* 无符号与有符号混合:当无符号类型和有符号类型混合时,如果无符号类型的范围大于或等于有符号类型,有符号类型会转换为无符号类型。
* 浮点提升: `float`会被提升为`double`
* 整型和浮点型混合: 整型会被转换为浮点型

1.2 数组转换成指针

很多用到数组的表达式中,数组会自动退化成指向数组首元素的指针:

int ia[10];
int* ip = ia; // ia转换成指向数组首元素的指针

不过存在一些例外情况,以下这些情况数组不会自动转换为指针:

  1. sizeof
    sizeof返回整个数组的大小,而不是指针的大小。
size_t size = sizeof(ia);
  1. &取地址运算符
    当对数组使用取地址运算符&时,结果是指向整个数组的指针,而不是指向单个元素
int (*pa)[10] = &ia; // pa 是指向含有10个整数的数组的指针
  1. 引用
    当数组用来初始化引用时,它不会转换为指针。也就是说允许引用绑定到整个数组。
int (&ref)[10] = ia;
  1. 模板参数
template<typename T>
void func(T param) {
    // ...
}

int ia[10];
func(ia); // T 是 int[10] 类型,不是 int*
  1. decltype运算符
    deltype返回表达式的确切类型,所以数组不会转换为指针。
decltype(ia) ib; // ib的类型是int[10]

1.3 指针的转换

  • 常量整数值0 和 字面值nullptr 可以转换成任意指针类型。
  • 指向任意非常量的指针能转换成 void*。
  • 指向任意对象的指针能转换成 const void*.
  • 继承关系的类型之间的转换。比如子类对象的指针可以隐式转换为父类对象的指针。

1.4 bool型转换

跟C语言一样。如果指针或算术类型的值为0,则转换结果为false;或者为true。

1.5 常量转换

TODO

1.6 构造函数或转换函数

单参数构造函数:具有一个参数的构造函数可以用来隐式地从该参数类型转换为类类型。
类型转换函数:可以实现一个类型转换函数将该类的实例隐式转换成其他类型。

#include <iostream>
#include <string>

class MyString {
private:
    std::string data;

public:
    MyString(const char* str) : data(str) {}

    // 类型转换函数,将 MyString 转换为 std::string
    operator std::string() const {
        return data;
    }
};

int main() {
	// 隐式转换,单参数的构造函数。这里直接将C字符串转换成了MyString类的实例。
    MyString myStr = "Hello, World!";
    
    // 隐式转换:MyString 通过类型转换函数转换为 std::string
    std::string stdStr = myStr;

    // 使用转换后的 std::string
    std::cout << stdStr << std::endl;

    return 0;
}

这里插一下explicit关键字,C++11引入,它可以用于构造函数,要求显式转换。如果上面MyString前面有explicit关键字的话,那么MyString myStr = "Hello, World!";是会报错的,必须显式转换。

二、显式转换

C++提供了四种显式转换运算符。

2.1 static_cast

用于非多态类型的转换。主要用在数值类型之间的转换,或派生类到基类的转换。

double d = 9.5;
int i = static_cast<int>(d); // 转换 double 为 int

2.2 dynamic_cast

主要用于处理多态性,它可以在类的继承层次中安全地进行上行转换(从基类到派生类),如果转换失败,它会返回nullptr。

class Base { virtual void dummy() {} };
class Derived: public Base { int a; };

Base *pBase = new Derived;
Derived *pDerived = dynamic_cast<Derived*>(pBase); // 这里将基类指针转换成派生类的指针是安全的

2.3 const_cast

const_cast用于改变对象的constvolatile属性, 它不改变对象本身的类型。

  • 移除const属性
    const int ci = 10;
    int *pi = const_cast<int*>(&ci); // 移除 const 属性
    
  • 添加const属性
    这种用法不太常见。
    void process(const int* ptr) {
        // 只读操作
    }
    
    int value = 10;
    process(const_cast<const int*>(&value)); // 将 int* 转换为 const int*
    
  • 类成员函数
    如果一个类的成员函数既有const版本也有非const版本,可以使用const_cast在它们之间转换。
    class MyClass {
    public:
        void func() const {
            // 做一些只读操作
        }
        void func() {
            // 可以修改对象状态
        }
    };
    
    const MyClass obj;
    obj.func(); // 调用 const 版本
    
    const_cast<MyClass&>(obj).func(); // 调用非 const 版本
    
  • 改变volatile限定符
    用的比较少,可能只有硬件或多线程相关编程的时候,有使用场景。
    下面假设reg是一个代表硬件寄存器的变量,默认以volatile的方式访问。
    int main() {
        volatile int reg= 42;
    
        // 正常访问: 访问被视为 volatile,每次访问都会重新从硬件读取
        std::cout << "Volatile access: " << reg<< std::endl;
    
        // 通过 const_cast 移除 volatile 属性
        int& nonVolatileReference = const_cast<int&>(reg);
    
        // 非 volatile 访问: 编译器可能优化这个读取
        std::cout << "Non-volatile access: " << nonVolatileReference << std::endl;
    
        return 0;
    }
    
    代码中使用const_cast来获得一个非volatile的对变量reg的引用,以便以非volatile的方式来访问它。

2.4 reinterpret_cast

提供一种低级转换,它可以将任何指针转换成任何其他类型的指针。它也可以用于证书和指针之间的转换。
这种转换可能导致运行时错误,使用时应非常谨慎。

long p = 51110980;
char *pc = reinterpret_cast<char*>(p); // 将 long 转换为 char* 类型

2.4 注意事项

显式转换应当小心使用,因为不当的使用可能导致数据丢失、类型不兼容、内存错误等问题。

尽可能使用 static_cast 和 dynamic_cast,因为它们相对更安全。尽量避免使用 reinterpret_cast,除非确实需要进行低级的指针操作。

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值