一文带你探索C++中类型转换的奥秘

 更新时间:2023年10月25日 14:02:54   作者:大神仙  
C++ 提供了四种类型转换方式,帮助我们在不同数据类型之间进行有效的数据传递和操作,这些类型转换方式在不同的场景下有各自的优势和适用性,下面我们就来深入了解一下吧

const_cast(常量转换)

const_cast(常量转换)是一种类型转换运算符,用于修改类型的const或volatile属性。它通常用于将const变量转换为非常量类型,或者将volatile变量转换为非volatile类型。用法如下:

const_cast<new_type>(expression)

new_type是你要转换成的类型,expression是要被转换的表达式。

示例

int main() {

    const int a = 10;

    //    int* pA = &a;//编译错误,不能将一个非常量指针指向一个常量对象。
    int* pA = const_cast<int*>(&a);
    *pA = 20;

    //输入10 20
    cout << "a:" << a << " *pA:" << *pA  << endl;
}

上面的代码定义一个 const int 类型的变量 a,我们想要将其转换为 int 类型。直接用一个指针pA指向这个a时,由于a本身是const类型,会导致编译错误。所以我们可以用 const_cast 把它做一个类型转换,去除 a 的 const 修饰符。然后修改 *pA 的值。

但是大家需要注意,最后的打印输出是a:10 *pA:20

即使你使用 const_cast 去除了指向 a的指针的常量性, *pA = 20 修改了*pA 的值,实际上并不会影响 a 的输出,这是 C++ 语言的常量性规则所决定的。因为它是一个常量。在编译时,编译器将 a 的值直接插入到代码中,而不是通过指针 pA 访问 a。因此无论如何修改 *pAa的值都保持不变。

const_cast 在某些情况下可以提高代码的可读性和减少冗余代码,但也存在一些缺点,例如增加代码的复杂度和容易产生歧义。

reinterpret_cast(重新解释转换)

reinterpret_cast是一种非常危险类型转换。它不会检查我们所指向的内容,也不会检查我们这个类型的本身。它只是做了一个重新的解释,我们要在转换的过程当中,保证它转换前和转换后占用的内存大小应该是一致的。用于在不同类型的指针之间进行转换,用法如下:

reinterpret_cast<new_type>(expression)

示例

int Test() {
    std::cout << "Test function called." << std::endl;
    return 0;
}

int main() {

    //创建一个 FuncPtr 的函数指针类型。指向没有参数和返回值的函数。
    typedef void(*FuncPtr) ();
    FuncPtr funcPtr;

    //    funcPtr = &Test;

    funcPtr = reinterpret_cast<FuncPtr>(&Test);
    funcPtr();


    return 0;
}

上述代码 Test 函数的返回类型是 int,而 FuncPtr 类型是 void(*)(),这两者的返回类型不匹配,因此不能将 Test 函数指针直接赋给 funcPtr。可以通过reinterpret_cast 显式类型转换来解决这个问题,funcPtr = reinterpret_cast<FuncPtr>(&Test)Test 函数的指针转换为 FuncPtr 类型的函数指针。

reinterpret_cast不进行类型检查非常灵活,可以在不同指针类型之间进行转换,包括将指针从对象类型转换为其他类型,或者在整数类型和指针之间进行转换。但是reinterpret_cast是非常危险的。因为他把这个检查的大部分工作交给程序员自身来管理的,可能导致转换不合理或不安全。

static_cast(静态转换)

static_cast(静态转换)可用于对基本类型进行转换,例如int *double *,有继承关系的类对象和类指针之间转换。要注意的是,如果用static_cast做一个继承关系的转换,安全问题需要由程序员自身来保障。

比如说动物,它有一个类叫鸭子,那么鸭子它一定是一种动物,所以它有动物的行为。但是动物未必都有鸭子的行为,所以如果我们把鸭子转换成动物,就是一个叫向上转换的过程。如果向下转换,那就是把动物转换成鸭子。但并不是所有的动物都是鸭子,所以这个地方就存在一些潜在的危险。如果我们使用static_cast就不会发现这样的问题。但如果我们使用dynamic_cast,就会帮我们检查出来这种错误。

示例

class Base
{
public:
    Base() : _i(0) {}
    virtual void T() { cout << "Base:T" << _i << endl; }
private:
        int _i;
};
class Derived : public Base {
public:
    Derived() : _j(1) {}
    virtual void T() { cout << "Derived:T" << _j << endl; }
private:
    int _j;
};

void main() {
    int intValue = 5;
    double intValue2 = static_cast<double>(intValue);
    cout << intValue << " " << intValue2  << endl;//5 5                  

    double dValue = 5.6;
    int dValue2 = static_cast<int>(dValue);
    cout << dValue << " " << dValue2  << endl;//5.6 5  丢失精度

    Base base;
    Derived derived;

    Base* pBase;
    Derived* pDerived;
	// 父类--》子类  static_cast未做检查 可能导致运行时错误
    pDerived = static_cast<Derived*>(&base);
    if (pDerived == NULL) {
        cout << "unsafe static cast from Base to Derived" << pDerived << endl;
    }
}

上面的static_cast将base转化为Derived*,可能导致运行时错误。所以在进行类型转换时,要注意类型匹配规则。确保转换是安全的并且符合预期。例如,在进行向下转型时,需要确保子类对象可以存储父类对象。

dynamic_cast (动态转换)

dynamic_cast (动态转换)用于在类的继承层次之间进行类型转换,它既允许向上转型,也允许向下转型。向下转型会进行检测,如果转换失败则返回空指针或抛出std::bad_cast异常。用法如下:

dynamic_cast<new_type>(expression)

new_type 和 expression 必须同时是指针类型或者引用类型。

示例

class Base
{
public:
    Base() : _i(0) {}
    virtual void T() { cout << "Base:T" << _i << endl; }
private:
        int _i;
};
class Derived : public Base {
public:
    Derived() : _j(1) {}
    virtual void T() { cout << "Derived:T" << _j << endl; }
private:
    int _j;
};

void main() {

    Base base;
    Derived derived;

    Base* pBase;
    Derived* pDerived;
    
	// 父类--》子类  pDerived输出NULL
    pDerived = dynamic_cast<Derived*>(&base);
    if (pDerived == NULL) {
        cout << "unsafe dynamic cast from Base to Derived" << endl;
    }
}

上述代码创建了一个基类 Base 和一个派生类 Derived,然后通过dynamic_cast,将基类指针Base转换为派生类指针 pDerived。,dynamic_cast 返回 NULL 表示转换失败。

在动态类型转换中,只能将一个指向派生类对象的指针转换为基类指针,而不是反过来。这是因为派生类对象包含了基类的部分,但反之并不成立。

如果想安全地将指向基类对象的指针转换为指向派生类对象的指针,要确保对象实际上是派生类对象。

总结

const_cast

用于转换指针或引用,去掉类型的const属性;

reinterpret_cast

重新解释类型,既不检查指向的内容,也不检查指针类型本身;但要求转换前后的类型所占用内存大小一致,否则将引发编译时错误。

static_cast

用于基本类型转换,有继承关系类对象和类指针之间转换,由程序员来确保转换是安全的,它不会产生动态转换的类型安全检查的开销;

dynamic_cast

只能用于含有虚函数的类,必须用在多态体系中,用于类层次间的向上和向下转化;向下转化时,如果是非法的对于指针返回NULL;

到此这篇关于一文带你探索C++中类型转换的奥秘的文章就介绍到这了,更多相关C++类型转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详谈C与C++的函数声明中省略参数的不同意义

    详谈C与C++的函数声明中省略参数的不同意义

    下面小编就为大家分享一篇详谈C与C++的函数声明中省略参数的不同意义,具有非常好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-11-11
  • 详解C++中static的用法

    详解C++中static的用法

    这篇文章主要介绍了c++中static的用法详解,C 语言的 static 关键字有三种用途,具体内容详情大家跟随脚本之家小编一起学习吧
    2018-05-05
  • C++ Qt开发之使用QUdpSocket实现UDP网络通信

    C++ Qt开发之使用QUdpSocket实现UDP网络通信

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,本文主要介绍如何运用QUdpSocket组件实现基于UDP的网络通信功能,需要的可以参考下
    2024-03-03
  • C语言与C++项目实现相互调用

    C语言与C++项目实现相互调用

    extern “c”的作用可以实现c语言和c++相互调用,本文就详细的介绍一下C语言与C++项目实现相互调用,感兴趣的可以了解一下
    2022-01-01
  • C语言位运算符的具体使用

    C语言位运算符的具体使用

    位运算是指按二进制进行的运算。在系统软件中,常常需要处理二进制位的问题。本文就详细的介绍一下,感兴趣的可以了解一下
    2021-09-09
  • C语言详解无头单向非循环链表各种操作方法

    C语言详解无头单向非循环链表各种操作方法

    无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多
    2022-04-04
  • C++实现动态烟花代码

    C++实现动态烟花代码

    这篇文章主要介绍了利用C++实现的放烟花程序,用到了EGE图形库,文中的示例代码讲解详细,对我们学习C++有一定帮助,需要的可以参考一下
    2023-01-01
  • C语言经典顺序表真题演练讲解

    C语言经典顺序表真题演练讲解

    程序中经常需要将一组数据元素作为整体管理和使用,需要创建这种元素组,用变量记录它们,传进传出函数等。一组数据中包含的元素个数可能发生变化,顺序表则是将元素顺序地存放在一块连续的存储区里,元素间的顺序关系由它们的存储顺序自然表示
    2022-04-04
  • StretchBlt函数和BitBlt函数用法案例详解

    StretchBlt函数和BitBlt函数用法案例详解

    这篇文章主要介绍了StretchBlt函数和BitBlt函数用法案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • c语言实现系统时间校正工具代码分享

    c语言实现系统时间校正工具代码分享

    这篇文章主要介绍了c语言实现系统时间校正工具,大家参考使用吧
    2014-01-01

最新评论