数据类型转换

一 隐式类型转换

1.1 算数转换

(+,-,*,/,%)
char , int, long, long long, float, double

15 + 3.14 => 15.0 + 3.14

1.2 赋值转换

#include <stdio.h>

int main(void) 
{
    int x; 
    // 计算结果31.4 转换为int类型,因为赋值符号的左边变量的类型是int类型
    x = 3.14 * 10.0; 
    cout << x << endl;  //31
    
    return 0;
}

1.3 输出转换

#include <stdio.h>

int main(void)
{
    printf("%c\n", 255+50);  // 305(0001 0011 0001)  ->  0x31(一个字节是8个位) ->  49 ('1');
    printf("%d\n", 255+50);  // 305
    return 0;
}

二 强制类型转换

2.1 直接使用数据类型

int* addr = (int*)0x003B66D0;  // 强制类型转换,整数直接变指针

2.2 c++新式类型转换

C++风格的类型转换提供了4种类型转换操作符来应对不同场合的应用。
格式:

TYPE b = 类型操作符<TYPE> ( a )
类型操作符= static_cast | reinterpreter_cast | dynamic_cast | const_cast

2.2.1 static_cast

静态类型转换(斯文的劝导,温柔的转换)。如int转换成char

主要用法:
用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。上行指针或引用(派生类到基类)转换安全,下行不安全
用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
把空指针转换成目标类型的空指针。
把任何类型的表达式转换成void*类型。

#include <iostream>

using namespace std;

class Animal
{
public:
    virtual void cry() = 0;
};

class Cat :public Animal
{
public:
    void cry() override
    {
        cout << "喵喵喵" << endl;
    }
};

class Dog :public Animal
{
public:
    void cry() override
    {
        cout << "汪汪汪" << endl;
    }
};

int main()
{
    // 1.用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。
    // 上行指针或引用(派生类到基类)转换安全,下行不安全
    Dog* dog1 = new Dog();
    Animal* a1 = dog1;                        // 隐式转换
    Animal* a2 = static_cast<Animal*>(dog1);  // 显示转换,子类的指针转成父类的指针
    Cat* cat1 = static_cast<Cat*>(a1);        // 不安全,把父类的指针转换成子类的指针 

    Dog dog2;
    Animal& a3 = dog2;
    Animal& a4 = static_cast<Animal&>(dog2);  // 显示转换,子类的引用转成父类的引用
    Cat& cat2 = static_cast<Cat&>(a3);        // 不安全,把父类的引用转换成子类的引用

    // 2.用于基本数据类型之间的转换,如把int转换成char
    double PI = 3.1415926;
    int num1 = PI;                            // 隐式转换
    int num2 = static_cast<int>(PI);          // 显示转换

    // 3.把空指针转换成目标类型的空指针
    int* p1 = NULL;
    int* p2 = static_cast<int*>(NULL);
    Dog* dp1 = NULL;
    Dog* dp2 = static_cast<Dog*>(NULL);

    // 4.把任何类型的表达式转换成void*类型。
    int* p = new int[10];
    void* vp = static_cast<void*>(p);
    return 0;
}
2.2.2 reinterpert_cast

重新解释类型(挂羊头,卖狗肉) 不同类型间的互转,数值与指针间的互转

用法: TYPE b = reinterpret_cast<TYPE>( a )
TYPE必须是一个指针、引用、算术类型、函数指针.

忠告:滥用 reinterpret_cast 运算符可能很容易带来风险。 除非所需转换本身是低级别的,否则应使用其他强制转换运算符之一。

#include <iostream>

using namespace std;

class Animal
{
public:
    void cry()
    {
        cout << "Animal cry" << endl;
    }
};

class Cat :public Animal
{
public:
    void cry() 
    {
        cout << "喵喵喵" << endl;
    }
};

class Dog :public Animal
{
public:
    void cry() 
    {
        cout << "汪汪汪" << endl;
    }
};

int main()
{
    // 1. 数值与指针之间的转换
    //int* p = static_cast<int*>(0xFFFF1234);   // 报错,类型转换无效
    int* p = reinterpret_cast<int*>(0xFFFF1234);
    int val = reinterpret_cast<int>(p);

    // 2. 不同类型指针和引用之间的转换
    Dog dog1;
    Animal* a1 = &dog1;
    Animal& a2 = dog1;

    Dog* pdog1 = reinterpret_cast<Dog*>(a1);
    Dog* pdog2 = static_cast<Dog*>(a1);          // 如果能用static_cast,优先使用static_cast
    Dog& pdog3 = reinterpret_cast<Dog&>(a2);

    Cat* pcat1 = reinterpret_cast<Cat*>(a1);
    Cat* pcat2 = static_cast<Cat*>(a1);          // 因为这里有继承关系,所以这里可以使用static_cast

    //Cat* pcat3 = static_cast<Cat*>(pdog1);     // 报错,类型转换无效 不同类型指针转换不能使用static_cast
    Cat* pcat4 = reinterpret_cast<Cat*>(pdog1);  // 这样转是有风险的

    return 0;
}
2.2.3 dynamic_cast

动态类型转换:
将一个基类对象指针cast到继承类指针,dynamic_cast 会根据基类指针是否真正指向继承类指针来做相应处理。失败返回null,成功返回正常cast后的对象指针;
将一个基类对象引用cast 继承类对象,dynamic_cast 会根据基类对象是否真正属于继承类来做相应处理。失败抛出异常bad_cast

注意:dynamic_cast在将父类cast到子类时,父类必须要有虚函数一起玩。

#include <iostream>

using namespace std;

class Animal
{
public:
    virtual void cry() = 0;

};

class Cat :public Animal
{
public:
    void cry() 
    {
        cout << "喵喵喵" << endl;
    }
    void play()
    {
        cout << "cat play" << endl;
    }
};

class Dog :public Animal
{
public:
    void cry() 
    {
        cout << "汪汪汪" << endl;
    }
    void play()
    {
        cout << "dog play" << endl;
    }
};

void animalPlay(Animal* animal)
{
    animal->cry();
    Dog* pDog = dynamic_cast<Dog*>(animal);
    if (pDog)
    {
        pDog->play();
    }
    else
    {
        cout << "不是dog" << endl;
    }

    Cat* pCat = dynamic_cast<Cat*>(animal);
    if (pCat)
    {
        pCat->play();
    }
    else
    {
        cout << "不是cat" << endl;
    }
}

void animalPlay(Animal& animal)
{
    animal.cry();
    try
    {
        Dog& dog = dynamic_cast<Dog&>(animal);
        dog.play();
    }
    catch(std::bad_cast bc)
    {
        cout << "不是dog,那应该是cat" << endl;
    }

    try
    {
        Cat& cat = dynamic_cast<Cat&>(animal);
        cat.play();
    }
    catch (std::bad_cast bc)
    {
        cout << "不是cat,那应该是上面的dog" << endl;
    }

}
int main()
{
    cout << "test 指针" << endl;
    Dog* dog1 = new Dog();
    Animal* al = dog1;

    Cat* cat1 = new Cat();
    Animal* a2 = cat1;

    animalPlay(al);
    animalPlay(a2);

    cout << endl << "test 引用" << endl;
    Dog dog2;
    animalPlay(dog2);
    Cat cat2;
    animalPlay(cat2);

    return 0;
}

image

2.2.4 const_cast

去const属性。(仅针对于指针和引用)

#include <iostream>

using namespace std;

void fun1(const char* p)
{
    // 1.对指针去掉const,再重新赋值
    char* p1 = const_cast<char*>(p);
    p1[0] = 'A';

    // 2.直接去掉const修改
    const_cast<char*>(p)[1] = 'B';
}

void fun2(const int& value)
{
    const_cast<int&>(value) = 5;
}

int main()
{
    cout << "test 指针" << endl;
    char arr[] = "12345678";
    fun1(arr);
    for (int i = 0; i < sizeof(arr) / sizeof(char); i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;

    // 常量字符串是不能去掉const修改的,因为常量字符串是存在常量区的
    // 在去掉常量限定符之前,保证指针所指向的内存能够修改,不能修改则会引起异常
    //const char* cP = "hello";
    //fun1(cP);  // 报错,常量字符串不能修改

    cout << endl <<"test 引用" << endl;
    int num1 = 1;
    const int &num2 = num1;
    fun2(num2);
    //const_cast<int&>(num2) = 2;
    cout << num1 << endl;

    return 0;
}

image

三 类型转换使用建议

1)static_cast静态类型转换,编译的时c++编译器会做编译时的类型检查;隐式转换;
基本类型转换,父子类之间合理转换

2)若不同类型之间,进行强制类型转换,用reinterpret_cast<>() 进行重新解释
建议:C语言中能隐式类型转换的,在c++中可用 static_cast<>()进行类型转换。因C++编译器在编译检查一般都能通过;C语言中不能隐式类型转换的,在c++中可以用 reinterpret_cast<>() 进行强制类型解释。

总结:static_cast<>()和reinterpret_cast<>() 基本上把C语言中的 强制类型转换给覆盖,注意reinterpret_cast<>()很难保证移植性。

3)dynamic_cast<>(),动态类型转换,安全的虚基类和子类之间转换;运行时类型检查

4)const_cast<>(),去除变量的只读属性

最后的忠告:程序员必须清楚的知道: 要转的变量,类型转换前是什么类型,类型转换后是什么类型,转换后有什么后果。
C++大牛建议:一般情况下,不建议进行类型转换;避免进行类型转换。

posted @ 2022-04-07 05:46  荒年、  阅读(55)  评论(0编辑  收藏  举报