0%

串讲:基本关键字、类型转换、运行时类型识别、重载运算、OOP、模板泛型

2020年4月26日 上午9:37

基本关键字:

  • C++这个框架提供给用户的功能接口。也需要并不像我们熟悉的开发框架,是通过给函数传参数进行功能的封装和调用,语言框架他是利用设计的语法 + 编译器的处理,最终达到的功能接口的作用。框架思想是理解C++的关键角度之一。
    附:
  • #define和inline 的区别 - cbwcwy - 博客园

类型转换:

  • 首先,需要说明的是类型和类别的区别
  • 另外,类型转换分为两个大类,编译器自动转换,手动关键字转换。自动类型转换对于不同的语法类型,编译器本身就定死了一些规则,当满足条件触发时,就会按既定的步骤进行类型转换。而手动类型转换,需要我们使用一些运算符,一般来说,如果在开发过程中需要使用手动类型转换才可以完成一些功能时,都是我们不合理的设计代码引起的,这时我们需要去修改我们原有的设计,而不是去使用手动类型转换

运行时类型识别:

  • 只有一种使用场景:我们有基类的指针,需要在继承类中调用基类中没有标志位virtual的成员方法。
    • 编译器也给我们留了对应的功能接口:dynamic_cast运算符。但是一般来说,出现这样的需求时,我们可以通过合理的重新设计代码逻辑,从而不需要这样的接口。
  • 关于typeid运算符
    • 在编译阶段会自动插入一段类型检查代码,这些代码会在运行时进行执行,从而判断对象的类型。这种运算符在python中其实很多很多
    • 从这个例子中我们就可以看出C++虽然是一门静态类型的语言,类型是在编译期进行确定,但是也可以支持动态类型语言在运行期做类型的判定和检查

OOP面向对象编程

  • 这个框架功能的设计就比上面提到的通过关键字、运算符来提供接口实现的功能要复杂。关键字、运算符这些语法点,可以认为在编译器代码中会使用一些映射好的代码进行替换,这些关键字、运算符其实更多的是为了帮助使用者将常用的功能封装好,这样就减少了重复劳动,和使用定义函数、模板、类进行代码复用的思想是一致的,只不过这些关键字、运算符更关注与系统功能的复用,而使用者更关心与逻辑功能的复用。
  • 相反,OOP就不是简简单单的代码替换这么简单,OOP可以把它理解成C++这门框架中的一个子系统
    • 既然提到了系统这个词,我们回想操作系统知识,那么这其中一定包括一定程度的层次抽象,就像linux中包含用户态和内核态一样,当用户态调用了一个系统调用以后,我们需要深入的了解这个系统调用在内核态的工作原理是什么。同样,在C++中我们需要知道OOP这个子系统到底是如何完成功能的
  • 对于编译器来说,资源的单位:变量 ,函数
    • OOP是对变量和函数这两个资源单位的逻辑上的组合,注意这里强调的是逻辑,这是为了更加贴近人类文明中的概念,减少语言框架和使用者之间的逻辑gap,让使用者能够更好的对现实进行抽象。而,对于编译器本身来说,他不需要这些人类社会的抽象,他眼中认为他自己所在的社会就是一个资源管理的系统,资源的单位时变量和函数,而即使是编译器,他也需要通过变量名和函数名和资源的映射表,来找到这些资源。
  • 虽然对于编译器来说,资源的单位是变量和函数,但是由于C++支持了OOP编程,那么资源单元之间就可以定义新的组合关系,并在组合关系之上设计新的功能,比如说:定义继承的访问控制、定义类的作用域、虚函数实现动态绑定机制,抽象基类等等新的功能。这些功能的本质依然是操作资源的基本单位,但是新添加了一些新的设计,花样也更多。
    • 这里需要特别强调关键的一点是:对于C++框架的使用者来说,视角是关注现实事物到类之间关系的转化抽象定义,但是对于C++框架本身的编译器来说,它的视角是更多的是从真实存储(内存的各个段,cpu的各种寄存器)的角度来分析。
    • 在编译器视角中,语法层的继承关系变成了资源单位之间的访问关系,这这一点特别关键。从内存角度来说:派生类对象(所占的内存)中含有与其基类对应的部分,这是继承的关键,其中提到的“含有”就是编译器通过代码实现的逻辑功能,将不同的独立的单元产生逻辑上的语义。

重载运算

  • 重载运算是OOP系统中其中一个支持的功能,为什么重载运算可以单独成为一章节呢?
    • 这里面的原因我认为是计算机的核心功能是完成计算,那么语言框架、编译器处理支持代码(变量、函数)的管理以外,也应该直观的可以进行运算接口。
  • 让自定义的类有运算能力:
    • 在C++已有类型的运算不需要我们程序员关心是因为默认的编译器已经在通识的基础上写好了,并且大家对加法、减法等有统一的认识,因此可以直接写到编译器中,程序员不用管了。那么,OOP中的自定义类,这时我们就需要结合自己对业务功能的理解,定义符合业务特有逻辑的运算操作。
  • 另外,对于重载运算来说更多是编程实战best prictise
    • 哪些运算符不应该被重载
    • 选择作为成员或者非成员

模板泛型

  • 要理解模板的意义,我们需要和define进行对比,我们知道define他只干一件事:在预处理期进行直接、简单的替换操作。怎么评价define呢?往好里说,define功能简单但是也很明确,人们很好理解KISS原则,往不好里说,功能也谈简单了,感觉一点高级功能都没有,连基本的类型、语法检查都没有。
  • 与difine相比,模板看上去好像也是简单的文本替换,但是在编译器中加入了一些高级功能:
    • 函数模板:可以进行自动的类型推断
    • 支持所谓的模板重载:SFINAE机制
    • 支持可变参数:编译器中加入了参数包的处理代码
    • 模板的特例化:对比与OOP中的类继承关系,特例化是另一种意义上的“关系描述方式”