《高性能MySQL》

        之前我对于MySQL的认识只是停留在会用的层面,虽然上一个游戏用的数据库就是MySQL,但也只是满足了使用需求,没怎么深入了解。这段时间读了《高性能MySQL》这本书,深入了解了MySQL并且学习到了如何更好的运用数据库。这本书的阅读基础是建立在读者对数据库有一定的使用经验上的。全书一直围绕着如何用好MySQL的这一主题来展开各个模块的内容,其中包括MySQL的底层机制、如何结合需求建表、各种数据类型的特性、索引的使用、查询语句优化、如何测试数据库、如何备份还原数据库以及各种工具的使用等等。下面会列一些比较重要的知识点作为记录,供大家参考。

Continue reading

C++ 数据对齐 问题

上一篇文章提到了数据对齐问题,那我们就来看看C++数据对齐的规则,举例分析一下,最后看一个由数据对齐引起的BUG,并思考其带给我们的意义。

首先为什么要数据对齐?现代的64位计算机,总线位宽为64个比特(bit),即8个字节(byte),CPU的寄存器也是8个字节大小,所以访问内存也总是以8个字节为单位。如果一份不足8字节大小的数据因为布局问题跨越了两个8字节单位,这样要读这份数据的时候,就要通过两次总线传输,传递16字节的数据,CPU也要分级缓存这16字节的数据,这实在是太糟糕了。同理,对于32位的系统,小于4字节的数据就不应该夸两个4字节空间,还有其他的平台可能数据访问一次只有2字节甚至1字节。综合这些,数据对齐就是要求数据应该排放在其大小的自然边界上,简单说就是大小为N(1,2,4,8,16,32)的数据不应该放在两个N格子之间,而应该放在一个单独的N格子之内。

上面解释了为什么要数据对齐,要对齐的情况好像还挺多的,也挺复杂的。我们把编译器关于数据对齐的策略总结为三条规则。

规则一:成员变量根据自己的大小来对齐。

规则一的意思就是,关于结构体内该成员是否要对齐,对齐到什么位置,不是看上一个变量,也不是看下一个变量,而是看自己。定义一个int,就要对齐到sizeof(int)整数倍的位置,定义一个double,就要对齐到sizeof(double)整数倍的位置。

规则二:结构体大小必须是最大成员变量大小的整数倍。

规则二定义的是结构体数组最后补齐的情况,如果一个结构体中定义了一个int与一个double,那么最大成员变量是double,大小为8。这时结构体虽然只有4(int) + 8(double)= 12字节大小,但会补齐为8的整数倍 16。这样做是为了处理结构体数组时,每一个结构体内的每一个成员变量都处于边界上。

结合规则一与规则二,我们就可以处理简单结构体的数据对齐了,先来看个例子:

Continue reading

利用C++特性 从内部成员地址索引外部对象

标题很纠结,来看看实际需求。我们经常说的class A 包含 class B有两种形式,一种是继承,一种是组合。对于这两种情况,假设我们现在得到class B 的地址,怎么通过这个地址反向找到class A 的地址呢?

首先来看看组合的情况:

wp1

对于GetExternalData()函数而言,它只拿到了内部成员MyClassB的地址,但想通过它访问外部的MyClassA的数据要怎么做到呢?首先我们要对于MyClassA的内存布局有一个概念:

Continue reading

漫谈设计模式 —— 职责的封装

前两篇文章介绍了创建封装的模式结构封装的模式,这次我们接着来看剩下的模式。分别有:策略模式、迭代器模式、命令模式、模板方法模式、观察者模式、责任链模式、状态模式、解析器模式、备忘录模式、访问者模式、中介者模式。这些模式的共同点是职责的封装。他们都是把一部分职责从原有的对象中抽离出来,封装成一个单独的对象,达到不同的目的。

最简单的就是策略(Strategy)模式

Strategy

策略模式就是把某一部分职责,例如角色的攻击抽离出来,单独封装成一个类,然后派生不同的实例,实现不同的攻击方式。这样原有的角色类就不再需要为攻击的多样化而操心了,利用接口的形式解耦后角色可以动态配上不同的攻击行为。

另一个常见的迭代器(Iterator)模式也是同样的思路,把遍历管理器内元素的职责从管理器中抽离出来,单独封装成一个迭代器类,这样可以使得管理器接口更加简单,并且只专注于管理元素。不同的是,根据管理器使用的容器结构,迭代器会有对应的一个实现,所以管理器与迭代器是绑定的,不会像上面策略模式那样动态替换。

以上两个模式都致力于对象与行为解耦,再推进一步,操作可变,并且操作的对象也可变,这样的需求该怎么组织呢?例如游戏正常状态下,按下键盘w键是控制角色往前移动,而在剧情模式下,按下键盘w键是控制摄像机围绕剧情所在场景转动。对于这种需求,命令(Command)模式可以派上用场。

Continue reading

漫谈设计模式 —— 结构的封装

上一篇文章谈了一些对创建行为封装的模式,还有一些模式他们是在结构层面来封装的,分别有外观模式、适配器模式、代理模式、装饰者模式、组合模式、桥梁模式与蝇量模式。什么是在结构层面封装呢?我们可以先简单的理解为在一个原有的类上面再包了一个类。那为什么要这样做呢?这样做可以达到很多灵活的变化,我们首先来看最直观也是最简单的,就是改变接口。

用一个类包裹一个原有的类,从而改变它的接口,这就是适配器(Adapter)模式。举个简单的例子,假设我们游戏中特效管理器已经有一个在场景内指定坐标播放特效的接口SFXMgr::PlaySceneSFX(int nX, int nY, int nZ)。现在我们有一个新需求是通过D3DVECTOR传递坐标,那我们第一个想法就是重载PlaySceneSFX,提供SFXMgr::PlaySceneSFX(const D3DVECTOR& vPosition)接口。如果再来一个需求,是在场景内一个物件脚下播放特效,这样我们又得重载一个SFXMgr::PlaySceneSFX(const SceneObject* pObject)接口。这样做不但把特效管理器改乱了,而且特效管理器还要看见D3DVECTOR和SceneObject。为了解决这样的问题我们可以写一个SFXMgrAdapter包裹着SFXMgr,然后由SFXMgrAdapter提供各种各样的接口,最后完成转换后调用到SFXMgr::PlaySceneSFX接口完成真正的功能。

适配器模式就是通过包裹一个原有的类,提供客户期待的接口,这样不需要改动到原来的类,也不用客户完成调用的转换细节。适配器有两种实现方式:类适配器和对象适配器。其实就是我们所说的“包裹”是以何种方法实现的。类适配器通过继承来包裹原有的类,提供新接口。对象适配器则通过组合,拥有原有类的一个实例。两种适配器都是为了改变接口,并且最终功能都是在原有类中完成,属于改变接口但不改变行为。

Continue reading