从Vector类的设计感受程序设计的艺术

来源:互联网 发布:linux中wget命令 编辑:程序博客网 时间:2024/05/25 19:55

游戏开发中离不开Vector类型,一般的项目都会多Vector类进行自己的封装(我见过的几个游戏引擎都是这样的),不同的人设计出来的Vector代码截然不同。那么具体来讲什么样的设计算是错误的或者不雅的?在今天晚上之前我一直以为这个问题很简单没有什么好思考的,因为Vector本身就是很简单的一个东西。今天晚上复习了一下《3D数学基础:图形与游戏开发》之后发现Vector的封装也能体现非常的程序设计艺术。当然世界上并不存在绝对完美的设计,但是我们还是尽可能的听听前辈们在这方面总结的一些经验吧。

1、float 与 double;

使用那种基本的c++数据类型来封装(x,y,z)?这个取决于我们的项目需求的精度,可以大致的计算一下32位float数中的24位小数能表示多么精确的精度时,我们就能根据项目的需求决定是采用那种数据结构了。如果您的项目不需要double的精度,那么使用float可以节省可观的内存资源并且获得更好的性能。

2、运算符重载,不要重载过多的运算符;

需要重载那些c++运算符?只有在操作符的意义特别明确时才重载它,如向量乘除标量、向量求反、向量加法和减法、向量点乘。值得注意的是下面几种操作不要重载操作符,应该提供函数代替:叉乘、求模、关系运算符、分量方式乘除法。

3、仅提供最重要的操作;

有些操作容易引起程序员犯错误,提供还不如避免。如将标量转换到向量的操作,取得向量的最大最小分量等。如果使用数组来定义向量则需要提供下标运算符或者GetX()函数,避免外部使用float*来访问变量。

4、使用const成员函数,使用const引用参数

这个没有什么好说的啦,一般的c++程序员都能理解,减少非必要的数据Copy操作,有助与提高效率,同时也避免函数修改引用参数的值。

5、成员函数和非成员函数;

使用成员函数还是非成员函数的基本原则是代码清晰,能使用非成员函数时就不要使用成员函数。下面的几个操作应该设计成非成员函数:求模、叉乘、求两点间的距离。

6、无缺省初始化;

构造函数中不要对向量做初始化,这和c++的内建(int、float)类型是一样的,声明了一个Vector之后再手动初始化。如果非要初始化那也要初始化为零向量。

7、不要使用信息屏蔽;

所有的变量float x,y,z;都定义为public类型的变量。大多数情况下使用private定义成员变量是明智的。3D向量的表达非常直观,如果在定义GetX()、SetX()函数只会使代码变得复杂,反而失去了简捷和效率。

8、全局常量:零向量;

声明一个全局常量:零向量,用来向函数传递零向量或者向量初始化,不需要每次都使用Vector(0,0,0)来构造零向量。

9、不存在“Point3”类;

Point3和Vector3在数学上是同样处理的,因此没有必要提供Point3类。只会增加程序的复杂度,Vector3和Point3之间的转换也成了一个棘手的问题。当然我们可以定义Rotation(Pitch,Yaw,Roll)类,并提供Rotation类和Vector3类之间的转换操作。

10、优化

关于优化我们要遵循几个原则:1)向量类越简单越好;2)没有证据表明所谓的“优化”真正是显著的提高了性能就不要优化;3)“过早的优化是一切罪恶的根源”。

从Vector类的设计中看出程序设计中的诸多需要引起注意的细节,细节就是艺术,程序员的使命就是去谱写艺术感的程序。

作者:吴紫鄂(zewu@live.cn