在编程语言中通常会有类型的概念,我们所使用的C++也不例外,其为静态类型(与之对应的是动态类型,对象的类型在运行时确定,其类型也可以动态改变)系统,所有对象、变量(包括常量)都得在编译时确定类型,并确定后该对象、变量的类型将不能改变。
静态类型在编译时已确定,其是固定的;而对象是个运行时概念,其是灵活的。一旦程序运行后,就没什么类型的概念了。那么我们为何还需要类型呢?
1.1类型的作用
C语言解决了B语言存在的一个严重的问题----类型问题。最初B语言是按字长取址,其运行机器也比较简单,那时候语言的唯一数据类型为word或者cell。当时还没有类型的概念,只是存储数据的一个单位罢了。直到PDP-11计算机的出现暴漏了B语言模型的不足之处。
首先,它不适合处理单字节。需要将字节打包到cell上,而且读写时涉及重组这些cell;其次,PDP-11计算机支持浮点运算,B语言为了支持浮点运算引入特殊的操作符,而这些操作符是硬件相关的,最后是B语言对指针处理有额外的开销,指针作为数组的索引,需要运行时调整数组的下标才能被硬件所接受。从这些问题里可以看出类型系统的重要性,它可以让编译器生成正确指令以及对应数据的存储方式。
后来类型系统的重要性更多体现在类型安全、类型检查上。类型不仅能够给数据赋予意义,还能充当接口,对行为进行约束。做一个让大量的猴子编写程序的思想实验,每个猴子每次随机敲下键盘的按键,然后编译、运行。
如果这些猴子采用的是机器语言,那么每个字节的组合都可能被图灵机解释并执行,只不过这样的执行结果将毫无意义。而高级语言(例如C++语言)有自己的词法、语法规则,输入的字节组合能够被编译器检查,那么凭借这样的功能在运行前能拦住很多无意义的字节组合,最终可运行的程序或多或少都有意义。例如定义了一个“人”的类型,那么其对象的运行过程中不会变成狗,也不会飞。
这里的类型检查就能阻止很多无意义的程序,与动态类型语言相比,在运行时所检查出的因类型而引发的错误都能够在编译时发现,这能节省很多程序调试的时间,软件开发过程中往往涉及重构,这些重构可以在保证功能不变的情况下,使得软件的可维护性更好。静态类型语言在重构后能及时发现类型错误,例如通过重构函数的形参类型,在编译时便能找出所有调用者,从而避免了遗漏。有些观点是在动态类型语言中通过添加大量测试来避免了因为类型问题而导致的错误,而测试本身往往是非确定性的,是个证伪的过程。当然仅通过类型系统是不可能完全保证程序的正确性,但至少在一定程度上保证程序的正确性。
1.2 现代C++中对类型处理能力的演进
C++在演进过程中逐渐增强和扩展了对类型处理的能力。
在C++11中引入了右值引用,从而重新定义值类别(value category)来对表达式进行分类,右值引用能够表达移动语义,解决了传统C++产生的中间临时对象需要多次拷贝的问题;引入了强枚举类型特性,约束了枚举值的域,同时不允许隐式转换为数值类型,也不允许不同枚举类型之间比较。相对普通枚举类型来说能够避免一些意外的bug;放松了对union特性的约束,使其能够与非平凡类组合,增强了实用性;引入了auto关键字,对初始化变量进行类型推导,减轻了程序员需要手写复杂类型(诸如迭代器类型)的负担;引入了decltype特性,通过已有对象、变量获取其类型,解决了难以声明一个对象的类型(诸如lambda对象类型)的问题;引入了nullptr_t类型,避免了整数类型与指针问题导致的重载歧义问题。
在C++17中引入了optional类型来表达一个对象是否存在的概念;引入了variant作为类型安全的union,使这些类型的表达更容易、更正确。
在C++20中引入concept特性来对类型做约束,如此无论是从代码角度上,还是从编译错误信息的角度上,都更加可读。
由C++标准委员会维护的C++ Core Guidelines里也针对类型提出了很多有启发性的建议,同时提供GSL基础库用于支撑这些指导方针,它是相当轻量的基础库,实现了遵循零成本的抽象原则。一些建议,例如C语言风格接口void f(T*, int),需要同时传递指针与其长度信息。若用区间类型gsl::span<T>代替前者即 void f(gsl::span<T>),则会更加友好;如果能够保证函数通过指针传递的参数非空,那么与其每次都用 void f(T*),对指针进行判空,不如将接口设计成 void f(gsl::not_-null<T*>)。
1.3 值类别
标签:语言,对象,C++,第一章,引入,类型,指针 From: https://www.cnblogs.com/Super-biscuits/p/17515430.html