• C++中的类型转换



    一、隐式类型转换

     
    隐式类型转换,顾名思义,就是没有明显的声明要进行类型转换,隐式类型转换有可能造成数据精度的丢失,所以通常所做的类型转换都是从size小的数据到size大的数据。
     
    我们以数字为例,在程序中见到的所有整形立即数都被当作int类型,所有浮点数立即数都被当作double类型,如下所示:

    int main() {
        auto a = 12.34;
        auto b = 100;
        std::cout << typeid(a).name() << std::endl; //double
        std::cout << typeid(b).name() << std::endl; //int
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在进行加减乘除等基本运算的时候,首先要保证运算符两边是同类型的数据,这时候,编译器会做隐式的类型转换,都是将数据从size小转到size大,这时不会损失数据精度,⚠️同时存在signedunsigned的时候,signed会转换成unsigned。其他的转换借鉴下图:
    在这里插入图片描述
    对象中包含的隐式转换

    1. 对象构造的时候使用如果编译器可以找到对象的构造函数,此时也会发生隐式的类型转换,但是通常不希望这种情况发生,可以在构造函数前加explicit关键字进行避免;

     

    二、显式类型转换

    这个没什么好说的,通过类型强转的方式,程序上能直接指定要转换成何种类型。但是需要注意一点的是,对象也能通过运算符重载函数强转成基本类型。使用方法如下:

    class A {
    public:
        explicit A(int ma) : _ma(ma) {
    
        }
        explicit operator double() const {
            double a = 13.34;
            return a;
        }
    private:
        int _ma;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

     

    三、c++风格的类型转换

    1. static_cast
      static_cast类似于强制类型转换,只是是一种tight type check的类型转换,而且是编译时期的类型转换。之所以说他是tight type check是因为static_cast不允许做一些不合理的类型转换,如下所示:
    #include 
    #include 
    using namespace std;
    int main()
    {
        int a = 10;
        char c = 'a';
      
        // pass at compile time, may fail at run time
        int* q = (int*)&c;  //clang编译通过
        int* p = static_cast<int*>(&c); //编译失败
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    另外在继承结构中,必须是public继承才能使用static_cast进行类型向上转换,如下所示:

    class Base {
    };
    
    class Derived : private Base { // Inherited private/protected not public
    };
    
    int main() {
        Derived d1;
        Base *b1 = (Base *) (&d1); // allowed
        Base *b2 = static_cast<Base *>(&d1); //编译失败
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

     
    2. dynamic_cast
    这种转换需要用到RTTI机制,关于RTTI的原理和对象内存布局,见我的另一篇博客C++对象内存布局
    在这里插入图片描述

    既然涉及到RTTI,那就必须有虚函数,如果继承结构中没有虚函数,那也就用不了dynamic_cast,dynamic_cast支持downcasting和upcasting,但是指针必须指向的是同一个对象,如果转换失败,dynamic_cast返回nullptr。使用方法如下:

    // C++ program demonstrate if there
    // is no virtual function used in
    // the Base class
    #include 
    using namespace std;
    
    // Base class declaration
    class Base {
    	void print()
    	{
    		cout << "Base" << endl;
    	}
    };
    
    // Derived Class 1 declaration
    class Derived1 : public Base {
    	void print()
    	{
    		cout << "Derived1" << endl;
    	}
    };
    
    // Derived class 2 declaration
    class Derived2 : public Base {
    	void print()
    	{
    		cout << "Derived2" << endl;
    	}
    };
    
    // Driver Code
    int main()
    {
    	Derived1 d1;
    
    	// Base class pointer hold Derived1
    	// class object
    	Base* bp = dynamic_cast<Base*>(&d1);
    
    	// Dynamic casting
    	Derived2* dp2 = dynamic_cast<Derived2*>(bp);
    	if (dp2 == nullptr)
    		cout << "null" << endl;
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

     
    3. reinterpret_cast
    顾名思义,重新解释的意思,这种类型转换将任意一种指针转换成另一种指针,这是一种危险的转换,使用方式如下:

    #include 
    using namespace std;
     
    int main()
    {
        int* p = new int(65);
        char* ch = reinterpret_cast<char*>(p);
        cout << *p << endl;
        cout << *ch << endl;
        cout << p << endl;
        cout << ch << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

     
    4. const_cast
    const_cast用于将变量的const约束去掉,const_cast并不像想象的那样可以直接修改const变量,const_cast去除掉的一般是指针的约束,使用方法见下:

    const int a=10;
    int b  = const_cast<int>(a); //这里会编译失败,因为实际上a的const限制是无法去除的
    
    • 1
    • 2
    int main(void) {
        int a1 = 40;
        const int *b1 = &a1;
        int *c1 = const_cast <int *> (b1); 
        *c1 = 100;   //此处没有问题,可以通过c1修改a1;
        *b1 = 1000; //此处编译失败,不能通过b1修改a1;
        cout << a1 << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如上所示,a1并不是const变量,只是我们定义了一个const指针,我们不能通过这个指针修改a1,但是我们可以把指针的const去掉,然后用去掉const限制的指针来修改a1。
     
    const_cast还可以用来在类的const函数中修改成员变量,因为在const方法中的this指针实际上是一个const指针,我们可以用const_cast去掉const限制,然后来修改成员变量,使用方法如下所示:

    class student
    {
    private:
        int roll;
    public:
        // constructor
        student(int r):roll(r) {}
      
        // A const function that changes roll with the help of const_cast
        void fun() const
        {
            ( const_cast <student*> (this) )->roll = 5;
        }
      
        int getRoll()  { return roll; }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    const_cast是一种比强转更安全的类型转换,比如将const int* 转为int是允许的,但是将const int转为char*是不允许的。
    const_cast还可以将volatile约束去掉,如下所示:

    int main(void)
    {
        int a1 = 40;
        const volatile int* b1 = &a1;
        cout << "typeid of b1 " << typeid(b1).name() << '\n'; //PVKi
        int* c1 = const_cast <int *> (b1);
        cout << "typeid of c1 " << typeid(c1).name() << '\n'; //Pi
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    机器学习——决策树
    二进制安装Docker
    【OpenCV图像处理9】图像金字塔
    线程间实现通信的几种方式
    Latex如何隐藏图片
    硬件寿命警告!Windows11在特定情况下对【固态硬盘】执行与【机械硬盘】相同的磁盘碎片整理。
    tiup cluster destroy
    对象、数组深拷贝与浅拷贝
    Layui + Flask | 表单组件(组件篇)(07)
    Oracle 表连接(内连接、外连接(左连接、右连接)、隐式连接、表并集、表交集、表补集)
  • 原文地址:https://blog.csdn.net/weixin_39861267/article/details/128143520