C开发人员的C ++类型转换

类型转换是使C ++具有更多类型安全性,更强大的功能,并且可以说服您在C上使用它。但是,当您是新手或从C背景移居时,这也是一个被低估的主题。 因此,我想出了一篇关于它的文章。 在这里,我们将不仅看到带有C开发人员示例的C ++类型转换,而且还将介绍为什么我们需要类型转换?C ++类型的铸造作弊代码,C开发人员可以轻松记住和使用它。

尽管我不是专家,但是到目前为止,这是我从各种来源和5年以上的行业经验中学到的。

在C ++中,有5种不同类型的强制类型转换:C样式强制类型转换,static_cast,const_cast,dynamic_cast和reinterpret_cast。

我通常以“我们为什么需要它?”开头,但是这一次我们首先快速了解一些术语,并以一些有关类型转换的CPP核心指南结束本文。

您需要面对的行话

  1. 隐式转换:编译器在其中自动进行类型转换。 像float f = 3;一样,在这里编译器不会抱怨,而是直接将整数类型3转换为float并分配给f。
  2. 显式转换 :开发人员使用强制转换运算符指导转换。 所有类型的手动转换都属于显式类型转换类别。 像int * p =(int *)std :: malloc(10);,这里我们显式将void *强制转换为int *。
  3. l-value :代表内存位置的标识符。 例如,变量名,* ptr(ptr指向内存位置等)。
  4. r值 :不是l值的值,r值出现在赋值(=)运算符的右侧。 喜欢
int a = 5 ; // 5 = r-value, 
q = p + 5 ; // p + 5 is r-value

注意:尽管在C ++中有一些例外和更多内容需要学习lvalue,rvalue及其引用

为什么我们需要类型转换?

  • 数据表示内存中的位(0和1)。
  • 数据类型是编译器指令,它告诉编译器如何存储和处理特定数据。uint32_t a = 5; 通过此语句,您可以假定将在内存中保留4个字节,并在执行时将在该内存位置存储0000 0000 0000 0000 0000 0000 0000 0000 0101数据位。 这很简单。
  • 让我们再进一步一点,浮点数f = 3.0; 该语句还将在内存中保留4个字节并以1)的形式存储数据位。 符号位2)。 指数&3)。 尾数。 回忆一下浮点数是如何存储在内存中的
  • 但是,当您编写类似float f = 3;的代码时,编译器会困惑于如何在float类型的内存中存储整数值。
  • 因此,它会自动假定(此处为隐式转换 )要存储3.0而不是3,从人的角度来看,这在技术上是相同的,但是从计算机内存的角度来看,它们的存储方式不同,这是不同的。
  • 在许多这样的方案中,您提供了要存储在内存中的数据,这些数据用来表示不同的数据类型。
  • 例如,在下面的示例中,您尝试将类型B的对象分配给类型A的对象
class A { };
class B { };
int main ()
 {
  B b;
  A a = b; 
  return 0 ;
}
  • 在这种情况下,编译器无法假定任何内容,只会引发编译错误:
exit status 1
error: no viable conversion from 'B' to 'A'
  A a = b;
    ^   ~
note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'B' to ' const A &' for 1st argument
class A {};
      ^
note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'B' to 'A &&' for 1st argument
class A {};
      ^
1 error generated.
  • 但是,当您按以下方式定义转换运算符时:
class B {
public :
  operator A () {
    cout << "CONVERSION OPERATOR\n" ;
    return A();
  } 
};
  • 编译器将简单地调用此成员函数并且不会引发任何错误,因为程序员明确提到这就是他/她想要转换的方式。

5种C ++类型转换以及C开发人员示例

1️C型演员表

int main ()  { 
    float res = 10 / 4 ;
    cout <<res<< endl ;
    return 0 ; 
}
  • 当您尝试运行上面的代码时,您会得到2作为我们没有想到的输出。 为了正确初始化res变量,我们需要使用float进行类型转换,如下所示:
float res = ( float ) 10 / 4 ;
  • 现在您的答案将是2.5。 这种类型的铸造看起来非常简单直接。
  • 您也可以在C ++中使用以下形式编写:
float res = float ( 10 ) / 4 ;
  • C样式强制转换可以更改数据类型,而无需更改基础内存表示,这可能导致垃圾结果。

2️ static_cast

  • 如果您是像我这样的C开发人员,那么这将是您最好的goto C ++ cast,它适合大多数示例,例如:
int * p = std :: malloc ( 10 );
  • 当您尝试使用C编译器编译以上代码时,它可以正常工作。 但是C ++编译器还不够好。 它将引发如下错误:
exit status 1
error: cannot initialize a variable of type 'int *' with an rvalue of type 'void *'
  int * p = std :: malloc ( 10 );
        ^   ~~~~~~~~~~
1 error generated.
  • 首先想到的是C样式的强制转换:
int * p = ( int *) std :: malloc ( 10 );
  • 这将起作用,但是在C ++中不建议使用这种样式的转换。 static_cast会像这样处理隐式转换。 我们将主要使用它在隐式转换失败的地方进行转换,例如std :: malloc。
int * p = static_cast < int *>( std :: malloc ( 10 ));
  • static_cast的主要优点是,它提供了编译时类型检查,这使得更容易出错。 让我们用C ++示例来理解这一点:
class B { };
class D : public B {};
class X { };

int main ()
 {
  D* d = new D;
  B* b = static_cast <B*>(d); // this works
  X* x = static_cast <X*>(d); // ERROR - Won't compile
  return 0 ;
}
  • 如您所见,在不了解所有涉及的所有类的情况下,没有简单的方法来区分这两种情况。
  • C样式转换的另一个问题是很难定位。 在复杂的表达式中,很难看到C样式的强制类型转换,例如T(something)语法等效于(T)something。

3️ const_cast

  • 现在,我们将直接跳至示例。 没有理论可以比例子更好地解释这一点。

1.忽略常数

int i = 0 ;
const int & ref = i;
const int * ptr = &i;

*ptr = 3 ; // Not OK
const_cast < int &>(ref) = 3 ;  //OK
* const_cast < int *>(ptr) = 3 ; //OK
  • 由于被分配给对象(此处为i)不是const,因此您可以修改i。 如果将const限定符添加到i,代码将进行编译,但是其行为将是不确定的(这意味着从“工作得很好”到“程序将崩溃>”。)

2.使用const this指针修改数据成员

  • const_cast可以通过将指针声明为const的方法来更改非const类成员。 -当基于const重载成员函数时,这也很有用,例如:
class X
{
public :
    int var;
    void changeAndPrint ( int temp) const
     {
        this ->var = temp;                    // Throw compilation error
        ( const_cast <X *>( this ))->var = temp; // Works fine
    }
    void changeAndPrint ( int *temp)
     {
        // Do some stuff
    }
};

int main ()
 {
    int a = 4 ;
    X x;
    x.changeAndPrint(&a);
    x.changeAndPrint( 5 );
    cout << x.var << endl ;
    return 0 ;
}

3.将const参数传递给仅接受非const参数的函数

  • const_cast也可以用于将const数据传递给不接收const参数的函数。 请参见以下代码:
int fun ( int * ptr) 
 { 
    return (*ptr + 10 ); 
} 

int main ( void ) 
 { 
    const int val = 10 ; 
    cout << fun( const_cast < int *>(&val)); 
    return 0 ; 
} 

4.被抛弃的挥发性属性

  • const_cast也可以用于丢弃volatile属性。 上面我们在const_cast中讨论的内容对于volatile关键字也有效。

4️ dynamic_cast

  • dynamic_cast在运行时使用类型检查,而static_cast则在编译时使用类型检查。 当您不知道它代表的输入类型时,dynamic_cast更为有用。 假设:
Base* CreateRandom ()
 {
    if ( (rand()% 2 ) == 0 )
        return new Derived1;
    else
        return new Derived2;
}

Base* base = CreateRandom();
  • 如您所见,我们不知道CreateRandom()在运行时将返回哪个对象,但是如果它返回Derived1,则要执行Derived1的Method1()。 因此,在这种情况下,您可以按以下方式使用dynamic_cast
Derived1 *pD1 = dynamic_cast <Derived1 *>(base);
if (pD1){
    pD1->Method1();
}
  • 如果dynamic_cast的输入未指向有效数据,则它将为指针返回nullptr或为引用抛出std :: bad_cast异常。 为了使用dynamic_cast,您的类必须是多态类型,即必须至少包含一个虚拟方法。
  • dynamic_cast利用了RTTI( 运行时类型识别 )机制。

5️ reinterpret_cast

  • reinterpret_cast是一个编译器指令,它告诉编译器将当前类型视为新类型。
  • 您可以使用reinterpret_cast将任何指针或整数类型转换为任何其他指针或整数类型。
  • 这可能会导致危险的情况:什么都不会阻止您将int转换为std :: string *。
  • 您将在嵌入式系统中使用reinterpret_cast。 适用于reinterpret_cast的常见情况是在uintptr_t与实际指针之间或在以下两者之间进行转换:
error: static_cast from 'int *' to 'uintptr_t'
      (aka 'unsigned long' ) is not allowed
        uintptr_t ptr = static_cast < uintptr_t >(p);
                        ^~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
  • 而是使用以下命令:
uintptr_t ptr = reinterpret_cast < uintptr_t >(p);
  • 我尝试涵盖了大多数复杂性,以阐明不同类型转换背后的主要概念,但是,仍然有可能我会错过一些。 因此,C ++类型转换就是这样,并为C开发人员提供了一个示例。 让我们快速回顾一下:

供C开发人员在类型转换时使用C ++的备忘代码

阅读完所有这些内容后,您可能会混淆使用什么以及何时使用! 这就是为什么我创建了这个作弊代码

  • 避免使用C样式强制转换。
  • 投放时请确定要什么。
  • 无论您使用的是C型转换,都应使用static_cast。
  • 对多态类使用dynamic_cast。 请记住,仅对继承层次结构中具有至少一个虚拟成员的类使用dynamic_cast。
  • 需要删除constvolatile限定符时,请使用const_cast
  • 如果没有选择,请使用reinterpret_cast。

注意:通常应避免使用const_castreinterpret_cast因为如果使用不正确,它们可能会有害。 除非您有充分的理由使用它们,否则不要使用它。

一些C ++核心准则

From: https://hackernoon.com/c-type-casting-for-c-developers-0c823y9k

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值