C++的转换手段并与explicit关键词配合使用

作者: 良知犹存

前言 {前言}

C中们会进行各种类型的强制转化,而在C中们经常可以看到这种转换

  memset(OTA_FLAG_ADDRESS,(uint8_t*)&OTA_Flag,sizeof(OTA_Flag));

而C++的类型转化和C差别很多,那么C++里面的类型转化是怎么用的呢。C++除了隐式转换和显示转化,显示转化是们熟知,有四个显示转化函数:static_castdynamic_castconst_castreinterpret_cast,主要运用于继承关系类间的强制转化。 {而c的类型转化和c差别很多,那么c里面的类型转化是怎么用的呢。c除了隐式转换和显示转化,显示转化是们熟知,有四个显示转化函数:static_cast、dynamic_cast、const_cast、reinterpret_cast,主要运用于继承关系类间的强制转化。}

下面就给大家说道说道。


作者:良知犹存

转载授权以及围观:欢迎添加微信公众号:羽林君


**隐式转化

**

c++语言不会直接将两个不同类型的值相加,二十先根据类型转化规则设法将运算对象的类型统一后再求值。例如 int value = 3.14 +3;这个程序是可以编译通过的,只不过编译器可能会警告改运算损失了精度。这样的类型转化是自动运行,无需程序员介入,因此,它们被称为隐式转换

**何时会发生隐式转化:

**在下面的情况中,编译器会自动的转化运算对象的类型:

  1. 在大多数表达式中,需要将比int类型小的整型值首先提升到较大的整数类型会进行隐式转化
  2. 在条件中,非布尔值转换成布尔类型
  3. 初始化过程中,初始值转化成变量的类型;在赋值语句中,右侧运算对象转化成左侧运算对象的类型
  4. 函数调用时候也会发生类型转化(实参类型转化)

显示转化

C风格的强制转换(Type Cast)容易理解,不管什么类型的转换都可以使用使用下面的方式

Type b = (Type)a;

当然,C++也是支持C风格的强制转换,但是C风格的强制转换可能带来一些隐患,让一些问题难以察觉.所以C++提供了一组可以用在不同场合的强制转换的函数.

C++提供了四种强制类型转化的函数,分别是: ****static_cast,命名上理解是静态类型转换。大部分C实现的转化,用这个函数就可以了。

const_cast ,字面上理解就是去const属性。
dynamic_cast ,命名上理解是动态类型转换。如子类和父类之间的多态类型转换。
reinterpret_cast,仅仅重新解释类型,但没有进行二进制的转换。

static_cast: {static_cast:}

任何具有明确定义的类型转化,只要不包含底层const,都可以使用static_cast,举一个例子。

double slop = static_cast<double>(j) / i;//进行强制转化成double
int*pn =&n;
double*dp = static_cast<double*>(&pn) //无关类型指针转换,编译错误
 void*p = &n;
 double*d = static_cast<double*>(&p) //将void*转化为初始的指针类型

​ static_cast强制转换只会在编译时检查,但没有运行时类型检查来保证转换的安全性。同时,static_cast也不能去掉expression的const、volitale、或者__unaligned属性。

const_cast: {const_cast:}

const_cast<>可以实现将const 指针类型转普通指针,const_cast操作不能在不同的种类间转换。相反,它仅仅把一个它作用的表达式转换成常量。它可以使一个本来不是const类型的数据转换成const类型的,或者把const属性去掉。如果一个对象本身不是一个常量,使用强制类型转化获得写权限是合法的行为。然而如果对象是一个常量,再使用const_cast执行写操作就会产生未定义的后果。

只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转化改变表达式的常量属性都将引发编译器错误。同样的,也不能用const_cast改变表达式的类型:

const char *cp;


char *q = static_cast<char *>(cp);//错误:static_cast不能转换掉const性质
static_cast<string>(cp);//正确:字符串字面值转换成string类型
const_cast<string>(cp);//错误:const_cast 只改变常量属性
const_cast<char*>(cp);//正确
const int p = 0;
int &rb = const_cast<int&>(p);//正确
rb =10;

dynamic_cast: {dynamic_cast:}

1.其他三种都是编译时完成的,dynamic_cast是运行时处理的,运行时要进行类型检查。

2.不能用于内置的基本数据类型的强制转换。

3.dynamic_cast转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL。

4.使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过。可以从父类转基类,但是可能为空

5.在类的转换时,在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。向上转换即为指向子类对象的向下转换,即将父类指针转化子类指针。向下转换的成功与否还与将要转换的类型有关,即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败。

class BaseClass { 
public: 
   int Num; 
   virtualvoid foo(){}; //基类必须有虚函数。保持多台特性才能使用dynamic_cast 
 }; 
DerivedClass: public BaseClass { 
public: 
  char*m_szName[100];
  void bar(){};
};
int main(int argc,char** argv)
{
  BaseClass* pb =new DerivedClass();
  DerivedClass *pd1 = static_cast<DerivedClass *>(pb); //子类->父类,静态类型转换,正确但不推荐
  DerivedClass *pd2 = dynamic_cast<DerivedClass *>(pb); //子类->父类,动态类型转换,正确
  BaseClass* pb2 =new BaseClass();
  DerivedClass *pd21 = static_cast<DerivedClass *>(pb2); //父类->子类,静态类型转换,危险!访问子类m_szName成员越界
  DerivedClass *pd22 = dynamic_cast<DerivedClass *>(pb2); //父类->子类,动态类型转换,安全的。结果是NULL

reinterpret_cast: {reinterpret_cast:}

实现指针转整形,整形转指针.reinterpret_cast是强制类型转换符用来处理无关类型转换的,通常为操作数的位模式提供较低层次的重新解释!但是它仅仅是重新解释了给出的对象的比特模型,并没有进行二进制的转换!
它是用在任意的指针之间的转换,引用之间的转换,指针和足够大的int型之间的转换,整数到指针的转换。最普通的用途就是在函数指针类型之间进行转换。
请看一个简单代码

int doSomething(){return 0;};
typedef void(*FuncPtr)(); //FuncPtr is 一个指向函数的指针,该函数没有参数,返回值类型为 void
FuncPtr funcPtrArray[10]; //10个FuncPtrs指针的数组 让们假设你希望(因为某些莫名其妙的原因)把一个指向下面函数的指针存入funcPtrArray数组:
funcPtrArray[0] =&doSomething;// 编译错误!类型不匹配,reinterpret_cast可以让编译器以你的方法去看待它们:funcPtrArray
funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething); //不同函数指针类型之间进行转换

explicit关键字(显示的类型转化运算符) {explicit关键字(显示的类型转化运算符)}

C++提供了关键字explicit,可以阻止不应该允许的经过转换构造函数进行的隐式转换的发生。即声明为explicit的构造函数不能在隐式转换中使用。

explicit关键字只能用于类内部的构造函数声明上,而不能用在类外部的函数定义上。现在Things类像这样:

class Things
{
public:
   Things(const std::string&name ="");
     explicit operator int() const{return val;} 
    //编译器不会自动执行这一类型的
};

下面再看个好一点的例子进行对比一下:

// 类的通过构造函数的隐式转换:include <iostream>
using namespace std;


class A {};


class B {
public:
  // conversion from A (constructor):
  B (const A& x) {}
  // conversion from A (assignment):
  B& operator= (const A& x) {return *this;}
  // conversion to A (type-cast operator)
  operator A() {return A();}
};


int main ()
{
  A foo;
  B bar = foo;    // 调用构造函数实现隐式类型转换
  bar = foo;      // calls assignment
  foo = bar;      // calls type-cast operator,相当于 foo = A(bar);
  return 0;
}

再看使用explicit的一个例子:

include <iostream>
using namespace std;


class A {};


class B {
public:
  explicit B (const A& x) {}
  B& operator= (const A& x) {return *this;}
  operator A() {return A();}
};


void fn (B x) {}  // 当们希望x只能是B类型时,们就需要禁止隐式类型转换


int main ()
{
  A foo;
  B bar (foo);  // 必须显式类型转换,不再允许B bar = foo; 
  bar = foo;
  foo = bar;


//  fn (foo);  // 不允许隐式类型转换
  fn (bar);  


  return 0;
}

这就是分享的c++的转化类型方法,其中参考了好多人的文字,此外如果大家有什么更好的思路,也欢迎分享交流哈。

***—*END*—*

推荐阅读

【1】c++nullptr(空指针常量)、constexpr(常量表达式)

【2】嵌入式底层开发的软件框架简述
【3】CPU中的程序是怎么运行起来的 必读
【4】C++的匿名函数(lambda表达式)
【5】阶段性文章总结分析

本公众号全部原创干货已整理成一个目录,回复[ 资源 ]即可获得.

参考链接:

C++ primer 第五版

https://blog.csdn.net/shuzfan/article/details/77338366

https://www.cnblogs.com/goodhacker/archive/2011/07/20/2111996.html

img点击并拖拽以移动

更多分享,扫码关注

原文作者:良知犹存

原文链接:https://www.cnblogs.com/conscience-remain/p/14076287.html

更多推荐

更多
  • 精通Go并发-十、高级并发和最佳实践 超越渠道基础,Building workers,Implementing nil channel blocks,对带有坟墓的 goroutine 实施更细粒度的控制,带通道超时,Building a load balancer with
  • 精通Go并发-四、应用中的数据完整性 通过互斥和同步进一步深化,goroutines 的成本,处理文件,越来越低–实施 C,分布式Go,一些常见的一致性模型,使用 memcached,cgo 中的触摸记忆,cgo 的结构,相反,分布式共享存储器,先进先出婴儿车,看主从模式,
  • 精通Go并发-五、锁、阻塞和更好的通道 了解Go中的拦网方法,清理 goroutines,创建渠道中的渠道,Pprof–又一个很棒的工具,处理死锁和错误,阻塞方法 1–监听、等待通道,检查我们的客户,阻塞方法 2–循环中的 select 语句,阻塞方法 3–网络连接和读取,通
  • 精通Go并发-八、并发应用架构 设计我们的并发应用,确定我们的需求,在 Go 中使用 NoSQL 作为数据存储,监控文件系统更改,管理日志文件,处理配置文件,检测文件更改,Backing up our files,设计我们的 web 界面,恢复文件的历史记录–命令行,
  • 精通Go并发-三、制定并行策略 复杂并发中的应用效率,用竞态检测识别竞态条件,同步我们的并发操作,项目多用户预约日历,多用户预约日历,Summary,使用相互排除,探索超时,可视化并发模式,开发我们的服务器需求,终点,自定义结构,一致性的重要性,Web server,
  • 六、C10K——Go 中的非阻塞 Web 服务器 攻克 C10K 问题,构建我们的 C10K 网络服务器,服务页面,多线程和利用多核,探索我们的网络服务器,服务器在 10000 个并发连接时发生故障,利用并发攻击 C10K,采取另一种方式,针对阻塞 web 服务器的基准测试,处理请求,
  • 精通Go并发-一、Go 并发介绍 引入 goroutines,执行延迟控制机制,理解 goroutines 与 corroutines,实施渠道,关闭和 goroutines,使用 goroutines 和 channels 构建网络蜘蛛,一个病人,戈罗廷,使用 Go
  • 精通Go并发-九、Go 中的日志记录和并发测试 Handling errors and logging,使用 log4go 包进行健壮的日志记录,Using the runtime package for granular stack traces,发布 goroutine 日志,使
  • 精通Go并发-二、理解并发模型 了解 goroutines 的工作原理,同步与异步 goroutine,可视化并发,RSS 正在运行,有点关于 CSP,去和演员模特儿,面向对象,使用并发,管理线程,使用同步和互斥锁锁定数据,设计 web 服务器方案,具有自我诊断功能的
  • 精通Go并发-七、性能和可扩展性 Go高性能,Using the App Engine,分布式Go,一些有用的图书馆,记忆保存,Summary,深入 pprof,并行性和并发性对 I/O 性能的影响,拓扑类型,硝基剖面仪,Heka,GoFlow,Go中的垃圾收集,类型
  • 近期文章

    更多
    文章目录

      推荐作者

      更多