本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循署名-非商业用途-保持一致的创作共用协议.
周末打麻将的时候和几个本科生一时兴起约了尬图书馆,第二天直接从寝室出发也没带书,准备去图书馆随便借点来看,我到了图书馆的时候他们还都在睡觉,看来我保住了研究生爱学习的良好形象,虽然打麻将什么的也是前晚熬夜打麻将是我带着他们去的(逃~)
去了图书加自习室的南馆四楼,计软分区,周末一天时间复习复习C++再好不过了。
《C++游戏编程入门》是之前是一本很基础但写的有很好的一本书,这本书虽说是游戏编程,但编写的都是控制台程序,说是游戏编程不如说是小程序例子下讲解C++,
书的内容不是很多,学过C++的大概三四个小时就能看完,
里面的例子很详细,全部都是C++的基础,很适合复习基础来食用,几乎每句代码都是一个知识点来展开谈,教科书式的晦涩语句也几乎没有,很多复杂的东西被几句话讲的很明白。
阅读本文可以帮助你迅速复习C++,了解和补充一些程序设计知识。
使用随机数
|
|
使用string对象
|
|
使用vector
|
|
其它STL容器
STL定义的容器分为两大类:顺序型和关联型。顺序型容器可以依次检索元素值,而关联型容器则基于键值检索元素值。
使用迭代器
|
|
使用算法
|
|
理解代码重用
- 提高产量 。通过重用已有的代码和一些元素的基础上(例如游戏引擎),可以迅速构建出完整的项目。
- 改善质量。 经过测试和已经确认无bug的代码进行重用,可以使程序稳定性增强。
- 改善性能。 一旦写出了或者改进后的高性能的代码,对其重用可以避免再造轮子,也避免了低性能代码的产生。
函数指定默认参数
在函数中的默认参数设置时,一旦在参数列表里指定了一个默认参数,则必须为余下的所有参数指定默认参数。
(此形参后面的所有参数都要设置上默认参数)
因此下面的原型是合法的:
|
|
而下面这个是非法的:
|
|
函数重载
实现函数重载,需要使用不同的形参列表为同一个函数编写不同的定义。
注意,仅仅是返回类型不同的函数,将导致编译错误。
例如:
下面是错误的:
|
|
下面是正确的:
|
|
程序规划
在编写一切代码之前,例如编写游戏,游戏设计者已在概念书、设计文档和原型上花费数不清的时间。一旦设计工作完成,程序员便开始他们的工作——更多的规划。只有在程序员写下他们自己的技术设计之后,他们才开始认真的编码。
一般流程:
编写伪代码
在抽象层面考虑代码,伪代码的每一行应当是一个函数调用。
之后,所有要做的则是编写伪代码所暗示的函数。
例如一个控制台版本的XXOO游戏,伪代码:
123456789101112131415161718create an empty xxoo boarddisplay the game instructionsdetermine who goes firstdisplay the boardwhile nobody has won and it's not a tie //tie代表平局局if it's the human's turnget the human moveupdate the board with the human's moveotherwisecalculate the computer's move //这里是计算出电脑该怎么行动,因为电脑是需要自己编写的AI,要根据人类的棋局选择下一步放在哪里update the board with the computer's movedisplay the boardswitch turns //转换选手congratulate the winner or declare a tie数据表示
有了一个不错的规划,但仍然很抽象,需要真正定义其中的元素。
例如:如何表示游戏棋盘?如何表示棋子和一招棋?
选择恰当的数据结构,去定义抽象的事物。
创建函数列表
伪代码暗示了所需要的不同函数,为他们创建一个列表,并确定各自功能、拥有的参数和返回值。
| 函数 | 描述 |
| —————————————- | —————————————- |
| void instructions(); | 显示游戏操作指南 |
| char askYesNo(string question); | 接受一个问题,返回“y”或“n” |
| int askNumber(string question, int high, int low = 0); | 询问一定范围内的数字。接受一个问题、一个范围上限和一个范围下限。返回low到high之间的数字 |
| char humanPiece(); | 确定玩家的棋子。返回X或O |
| char opponent(char piece); | 返回给定棋子的对应棋子。 |
| void displayBoard(const vector& board); | 在屏幕上显示当前棋盘。 |
| char winner(const vector& board); | 确定游戏的胜者。返回X、O、或T(和棋)或N(还没有哪一方胜出) |
| bool isLegal(const vector& board, int move); | 判断输入的数字是否合法 |
| int humanMove(const vector& board, char human); | 获取人类玩家的下棋。接受一个棋盘与人类玩家的棋子作为参数,返回玩家下棋的数字位置。 |
| int computerMove(vectorboard, char computer); | 获取计算机玩家的下棋。接受一个棋盘与人类玩家的棋子作为参数,返回玩家下棋的数字位置。 |
| void announceWinner(char winner, char computer, char human); | 宣布最后结果。 |
使用常量指针
声明一个常量指针,无法修改存储在常量指针中的地址
|
|
注意,和所有常量一样,第一次声明常量指针时候必须初始化
|
|
提示:在C++中尽管可以使用指针,但还是尽可能的使用引用,引用在语法上比指针更加简洁,并且让代码更易读懂。
使用指向常量的指针
|
|
指针本身可以变得,但是被指的对象为常量,不可改变。
使用指向常量的常量指针
|
|
注意
使用指针时候要注意野指针问题。
特别是函数返回类型为一个指针时,例如:
|
|
这段代码可能导致程序崩溃,因为返回的指针所指向的字符串在函数结束之后不复存在。成为野指针。
静态数据成员
- 静态变量在函数调用之间保留其值
- 类中定义的公有静态数据成员,可以在程序任意一处以加上类域的方式访问:
Critter::s
,只有静态成员变量可以这样访问! - 静态数据成员私有化的时候,就只能和其它私有数据成员一样,只能在类成员函数之中对其进行访问。
静态成员函数
- 静态成员函数是为整个类而存在的函数。
static int get_s();
- 静态成员函数主要用来使用静态数据成员。
|
|
静态成员函数不能访问非静态数据成员!
因为静态成员函数是为整个类而存在的,而与具体的该类的某个实例无关。
从外部调用调用类需要用类域名限定它:
Critter::get_s()
STL的reserve()
|
|
函数reserve()将字符串的容量设置为至少size. 如果size指定的数值要小于当前字符串中的字符数(亦即size < this→size()), 容量将被设置为可以恰好容纳字符的数值. reserve()以线性时间(linear time)运行。
它最大的用处是为了避免反复重新分配缓冲区内存而导致效率降低,或者在使用某些STL操作(例如std::copy)之前保证缓冲区够大。
对于指针和数组的关系
指针和数组名之间的关系记住这几个公式即可:
|
|
- $$array \equiv \&array[0]$$
- $array+k\equiv \&array[k]$
- $*array\equiv array[0]$
- $*(array+k)\equiv array[k]$
避免内存泄露
两种典型的导致内存泄露的例子:
|
|
分配了个内存块,函数没有返回内存指针,没办法释放,内存泄露。
|
|
此时没有任何指针指向堆中存储50那块内存,程序无法释放它,内存泄露。
深拷贝和浅拷贝
|
|
例:
|
|
类的访问权限
- public成员可以被程序中的所有代码访问。
- protected成员只能被本类与特定派生类访问,这取决于继承的访问级别。
- private成员只能被本类成员访问,即它们不能被任何派生类直接访问。
虚基类成员函数
对于任何继承的基类成员函数,如果期望在派生类中对其重写,则应当使用关键字virtual将其声明为虚函数。
尽管可以重写非虚成员函数,但这可能会导致一些意外行为,一个较好的准则是将任何要重写的基类成员函数声明为虚函数。
重写函数与重载函数
注意:在重写一个基类的重载成员函数时,基类成员函数的所有重载版本都会被覆盖掉,意味着访问其他版本的成员函数的唯一方式是显式的地调用基类成员函数。因此,如果要重写重载成员函数,最好重写重载函数的每个版本。
在派生类中使用重载运算符与拷贝构造函数
重载运算符与拷贝构造函数不会从基类继承过来,所以在派生类中需要重新处理。
在派生类中重载赋值运算符时,通常需要调用基类中的赋值运算符成员函数,方法是使用基类名称作为前缀显式调用。例:
Boss是从Enemy继承而来,那么Boss中定义的重载赋值运算符成员函数可以这样开始:
|
|
纯虚函数
当类包含至少一个纯虚函数时,该类为抽象类。