[读书笔记]----游戏引擎架构(三)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39630587/article/details/86744287

第三章 游戏软件工程基础

这一章节主要介绍了面向对象的相关概念,这对于本书的阅读及整个游戏开发都是很重要的。

3.1 重温C++及最佳实践

和对应的实例之间存有一对多的关系。
封装(encapsulation)是指对象向外只提供有限接口,隐藏对象的内部状态和实现细节。产生"是一个(is-a)"的关系
继承(inheritance)能借着延伸现有的类去定义新的类。新类可修改或延伸现有类的数据、接口和行为。Parent称为基类(base class)或超类(superclass),而Child称为派生类(derived class)或子类(subclass)。一些语言可以支持多重继承(multiple inheritance, MI),但是会产生致命的菱形继承问题。C++可以使用虚继承(virtual inheritance)去掉重复祖父类的数据。
多态(polymorphism)容许采用单一共同接口操作一组不同类型的对象。
合成(composition)是指,使用一组互动的对象去完成高阶任务。产生"有一个(has-a)“和"用一个(uses-a)”
设计模式,由四人组(Gang of Four, GoF)总结的23种设计模式。常用的有单例、迭代器、抽象工厂等。而游戏工业也有自己一套设计模式,以对付渲染、碰撞、动画、音频等各领域的问题。

3.2 C/C++的数据、代码及内存

定点记法:可以随意选择整数部分及小数部分各用多少位表示。但是,它同时限制了可表示整数部分的范围及小数部分的精度。
浮点记法:小数点可以任意移动至不同位置,此位置是有指数控制的。最流行的浮点标准为IEEE-754标准。
char通常是8位,足够存储一个ASCII或UTF-8字符。
int、short、long:int是有符号整数,通常定义为32位。short多为16位,long一般也为32位,有些平台为64位。
float是IEEE-754的32位浮点数
double是IEEE-754的双精度(即64位)浮点数
bool保存真/假值,有些编译器定义为8位,有些为32位。
大于8位(1字节)的值称为多字节量。在内存中处理多字节整数由两种不同的方式,不同的微处理器会有不同的大小端模式
对于游戏程序员来说字节序是不可忽律的问题。因为游戏通常是在英特尔CPU(小端)的PC上开发,而游戏可能执行于游戏主机,如Wii,Xbox 360,PlayStation 3——这三种主机皆使用PowerPC处理器的变种(可设置使用任意字节序,但预设是大端)
在C/C++语言中,变量和函数必须先声明(declare)再定义(define),然后才可以使用。声明是实体的引用,而定义是实体本身
每个C/C++的定义都有名为链接规范(linkage)的属性。外部链接的定义可被定义处以外的翻译单元看见并引用。内部链接的定义则只能被该定义所处的翻译单元看见,而不能被其他翻译单元引用。所有定义预设为外部链接。使用static关键字可以把定义改为内部链接。
无论是windows的.exe拓展名还是unix的.elf拓展名,这些可执行文件总是包含程序的部分映像(image),程序执行时此部分映像会置于内存中。之所以称为部分映像的原因是,由于程序除了把可执行映像置于内存中,一般也会分配额外内存。
映像文件至少由以下4个段组成。

  1. 代码段:包含全部函数的可执行代码
  2. 数据段:包含全部获初始化的全局及静态变量。
  3. BSS段:包含所有未初始化的全局及静态变量。
  4. 只读数据段(rodata段):包含程序中定义的只读变量(常量)全局变量。

全局变量——是指由所有函数及类声明外的文件作用域(file scope)定义的变量——按照是否被初始化,而决定存储于数据段或BSS段。
函数静态变量的词法作用域(lexical scope)只在其定义的函数之内(即变量的名字只能在函数内"见到")。
函数静态变量在第一次调用其函数时被初始化;文件域静态变量在main()调用前已被初始化。
当可执行程序被载入内存时,操作系统会保留一块称为程序堆栈(program stack)的内存。当调用函数时,一块连续的内存就会压入栈,此内存块称为堆栈帧(stack frame)。

堆栈帧存储3类数据:

  1. 堆栈帧存储调用函数的返回地址(return address)
  2. 堆栈帧保存相关CPU寄存器的内容
  3. 堆栈帧也包含函数里的所有局部变量(local variable),或称自动变量(automatic variable)

全局和静态变量分配于可执行映像里,而局部变量则分配于程序堆栈中。为了提供动态分配功能,操作系统会维护一块内存,当运行程序调用malloc()函数时就会从中分配,稍后调用free()可把内存交还。此内存块称为堆内存(heap memory)或自由存储(free store)。
谨记class和struct的声明并不占用内存,这些声明仅是数据布局的描述。

static关键字有许多不同的含义。

  1. 当用于文件作用域时,static意味着变量或函数的可见性(visibility),只有.cpp文件才能使用该变量或函数
  2. 当用于函数作用域时,static意味着"变量为全局,非自动,只在本函数内可见"
  3. 当用于struct或class声明时,static意味着"该变量非一般成员变量,而是类似于全局变量"

对象的内存布局会有对齐和包裹问题。
数据对象的对齐是指,其内存地址是否为对齐字节大小的倍数。现在许多处理器实际上只能正常地读/写已对齐的数据块,需要通过掩码(mask)和移位(shift)处理才能读写非对齐数据。
在这里插入图片描述
在这里插入图片描述
上图整个结构的大小为20字节,并非预期的18字节。这是由于在末端加进了两个字节的填充
在这里插入图片描述
游戏程序员可以在结构末位显示地加上填充,使得浪费的空间更加清晰。

3.3 捕捉及处理错误

所有软件项目皆有两类基本错误状况:用户错误(user error)和程序员错误(programmer error)。
所有问题皆无银弹(no silver bullet),程序开发者须按个别情况,判断采用哪种错误处理方法,以改善开发者的使用体验。

实现错误检测及处理

  1. 错误返回码:设计枚举值(enumerated value)以表明函数执行的成败。
  2. 异常:在游戏引擎中,有颇充分的理由去完全关掉异常处理。因为游戏主机的内存和效能都是有限的,游戏主机上的引擎大概永远不会使用异常。然而,为基于PC而开发的游戏引擎,可以安然使用异常。
  3. 断言(assertion):是指一行检查表达式的代码。断言是以宏来实现的,所以如有需要,只需改写该宏,就能在代码中去除所有断言。在开发期间,断言引起的性能开销,通常可以忍受。游戏发行时,可以去除断言,取回那一点关键效能。

猜你喜欢

转载自blog.csdn.net/qq_39630587/article/details/86744287