- 注册时间
- 2005-4-8
- 最后登录
- 1970-1-1
|
上一讲给了大家一个对于windows编写游戏的概括印象,这一次,我们将继续把windows编程概括着不动,转过来看看c++大概是个什么样子
一、class 关键字:
是的,这是个关键字,并且也仅仅是个关键字。这个关键字的一个用途,就是来定义类。关键问题是类是什么?大家可以暂时不要被OO的这种术语搞乱套,就把类当作一个带有函数的struct(不知道struct?就是结构,Pascal和QBasic中都有的东西,如果还是不知道,请参见最后Q&A)。这个struct使用起来其实和struct并无二致,下面是一段示例代码片段:- class Fool
- {
- public: //这个叫做访问控制,表示这个标签下的东西可以任意访问
- int a; //一个整数成员
- char p; //一个字符型成员
- private: //这个标签下的东西外界不能访问
- int p2;
- }; //不要忘了分号
- void Main(HWND hWnd)
- {
- //与c中的struct不同,这里Fool就是这个类型的全称,这里定义一个变量
- //类型为Fool,名字为f
- Fool f;
- //为这个变量分量a赋值
- f.a=10;
- //为这个变量中分量p赋值
- f.p='h';
- //注意这里不可以使用f.p2,因为p2是私有(private)的
- }
复制代码
要注意的就是class中的访问控制标签,共有public,protected,private三种,其中public标签下的东西可以被随意访问,private标签下的东西不能被类外的东西访问,至于protected标签,则表示只能被自己和自己的子类访问(关于子类,以后会讲到)
另外定义类的变量(专业些叫“实例”)的时候,只要直接给出类名加变量名就可以,c程序员要注意
而class这个关键字不是白引进的,类和传统意义的struct不同的最大一点就是类可以有方法(c++中叫成员函数,而“方法”在Java或者OO社区中叫得比较多)。就是说类是一个带有函数的结构。这个函数操纵的对象就是类中的各个成员,下面看一个例子:
- class Bean
- {
- //这里没有访问控制标签,则默认为private,也就是别人不能访问
- //m_x中的m_是一种命名习惯,表示这是一个成员变量(就是类中的变量)
- int m_x,m_y;
- public:
- //在public下的函数可以被外界访问
- void Draw(HDC hdc)
- {
- //这里m_x,m_y就是类自己的m_x,m_y
- ::Rectangle(hdc,m_x,m_y,m_x+10,m_y+10);
- }
- void Set(int x,int y)
- {//这里为访问m_x,m_y提供一种途径,但是是有条件的,对于正数才会赋值
- if(x>0 && y>0)
- {
- m_x=x;
- m_y=y;
- }
- }
- //下面这个函数比较古怪,名称叫构造函数(Constructor)
- //它的名字和类名一样,但是没有返回值,可以带参数
- //这个函数在创建类的变量的时候,就会对这个变量自动调用
- //下面这个意思就是在创造变量的时候把成员初时化一下
- Bean(int x, int y)
- {
- m_x=x;
- m_y=y;
- }
- }; //!!!!!分号
- void Main(HWND hWnd)
- {
- //定一一个Bean类型的变量
- //变量名后面的小括号其实就是上面那个构造函数的参数
- Bean bean(10,10);
- //调用成员函数其实和访问成员变量一样,都是加个点
- //只不过函数要有小括号
- //这里bean的m_x,m_y已经被构造函数初始化了,所以可以直接画
- bean.Draw(::GetDC(hWnd));
- //换个地方再画一个
- bean.Set(100,100);
- bean.Draw(::GetDC(hWnd));
- //再定义一颗豆子(bean),要注意c++可以在要用的时候定义变量
- //而不是在开始处全定义好
- Bean bean2(300,300);
- bean2.Draw(::GetDC(hWnd));
- }
复制代码
成员函数也受访问控制标签的控制,可以有私有(private)和保护(protected)成员函数。
构造函数是特殊的成员函数,不能被直接调用,而是在定义一个变量的时候被自动调用,他的参数就跟在变量名之后。如果你没有定义过构造函数,那么c++会帮你自动生成一个什么也不做的(当然你看不到),在初时化的时候会调用一下(当然,这只是简单的情况,大家不要死扣这句话,会有特例的)
类的情况就先讲这么多,足够大家看下面的代码了:)
二、STL
STL不是c++语法中的部分,他是Standard Template Library的缩写,就是传说中的标准模版库。这是c++中提供的一套标准的数据结构,算法以及很多好玩的东西堆积的库,大家现在只要拿来用就可以,至于以后嘛,建议把他的源码读一读,起码他的所有原理都要搞搞清楚。
STL中最常用的就是他的容器(Container),堆栈,链表等等给你写好的玩意,拿来即可用,相当的方便:
- //这里展示vector的使用,要包含这个头,注意用尖括号并且没有.h
- #include <vector>
- //表示打开命名空间std,暂时不用理会,一般使用标准库内容都要加这个
- using namespace std;
- void Main(HWND hWnd)
- {
- //定义一个vector,那个尖括号里面的东西表示这个vector中放int型的东西。
- vector<int> IntList;
- //往vector中加入一个int型的值:10
- IntList.push_back(10);
- //再加一个
- IntList.push_back(30);
- IntList.push_back(50);
- //注意这里:
- //IntList.size()表示这个vector的大小,现在放进去三个数字,就是3
- for(int i=0 ;i<IntList.size() ;i++)
- {
- //注意IntList的使用方法,他可以直接使用[]来表示他里面的第几个内容
- ::Rectangle(hdc,10,IntList[i],20,IntList[i]+10);
- }
- }
复制代码
好玩吧?
关于那个尖括号,那个是模版方面的东西,我暂时还不会做这方面的讲解,大家只要知道里面放一个类型名就可以,可以是int,可以是咱们前面定义的Fool,Bean之类的。来表示这个容其中放什么。
一般常用的这种容器有两个,一个就是这个vector,另外一个则是list。vector表现的是数组的意义,而list则表现了链表。这两个容器都有push_back,pop_back,insert等实现,而由于数组和链表结构的不同,他们也有自己不同的地方,比如vector就可以使用[]像数组一样直接访问内容,而list则要使用一种叫做迭代器的东西来逐个访问内容。(关于数组和链表有什么不同,请参见Q&A)
迭代器是什么?其实就是长得好像指针一样的东西,我这里也只给出示例,要彻底讲明白他是什么可是太困难了,大家会用就好:- #include <list>
- using namespace std;
- void Main(HWND hWnd)
- {
- list<int> List;
- List.push_back(10);
- List.push_back(30);
- List.push_back(50);
- //注意这里:
- //list<int>::iterator表示list<int>这中类型容器的迭代器
- list<int>::iterator i;
- //已经定义过i,这里就不再定义,只赋值:
- //List.begin()表示指向List中第一个位置的迭代器,就是头指针。
- //List.end()表示指向List中末尾的迭代器,这个迭代器是个假的,就是用来标识
- //结尾的
- //i++表示迭代器向下移动一个位置,和指针++的意义一样
- for( i=List.begin() ;i != List.end() ;i++)
- {
- //注意i的使用方法,他可以直接使用*来表示他指向位置的内容
- //和指针的用法一样
- ::Rectangle(hdc,10,*i,20,*i+10);
- }
- }
复制代码
大家可以试试把上面那段使用vector的代码中直接访问的代码改成使用迭代器:)
啊啊,打了这么多,累死了,本来还想说的一个内容放到下一次做专题吧,或者有谁愿意自学一下帮我写写:)
三、代码注释:
- #include "Window.h"
- #include "time.h"
- #include "Frame.h"
- //这里我们使用链表来表示蛇
- #include <list>
- using namespace WinApp;
- using namespace std;
- //几个常数,const表示常数,而static大家忽略就可以
- const int width=10,height=10;
- static const int dx[]={0,1,0,-1};
- static const int dy[]={-1,0,1,0};
- //蛇类型
- class Snake
- {
- //一个存放POINT型内容的链表
- list<POINT> m_nodeList;
- int m_dir;
- int m_width,m_height;
- //自己会保存一份HDC,方便
- HDC m_hdc;
- HBRUSH bkBrush;
- //屏幕的大小
- RECT m_rect;
- public:
- //构造函数
- Snake(int width,int height,int dir)
- {
- m_dir=dir;
- m_width=width;
- m_height=height;
- }
- //不过仍然需要初始化一下,因为需要一个HWND的参数,只有在Main中才能获得
- void Init(HWND hWnd)
- {
- m_hdc=::GetDC(hWnd);
- bkBrush=::CreateSolidBrush(::GetBkColor(m_hdc));
- ::GetWindowRect(hWnd,&m_rect);
- //随机产生一个点
- int rx=rand()%(m_rect.right-m_rect.left) / width * width;
- int ry=rand()%(m_rect.bottom-m_rect.top) / height * width;
- POINT begin;
- begin.x=rx;
- begin.y=ry;
-
- //把这个点放到POINT的链表中
- m_nodeList.push_back (begin);
- }
- //把蛇移动一步
- bool Move()
- {
- //back()表示取这个链表的最后一个节点的值
- POINT p=m_nodeList.back();
- RECT re;
- re.left=p.x;
- re.top = p.y;
- re.right=p.x+width;
- re.bottom=p.y+height;
- ::FillRect(m_hdc,&re,bkBrush);
-
- //front()表示第一个节点的值
- POINT front=m_nodeList.front();
-
- //pop_back()表示删去最后一个节点
- m_nodeList.pop_back();
- front.x+=dx[m_dir]*width;
- front.y+=dy[m_dir]*height;
- //insert表示插入一个节点,第一个参数是代表位置的迭代器
- //这里使用begin()表示插入头部
- //第二个参数是要插入的值。
- m_nodeList.insert(m_nodeList.begin(),front);
- //迭代器,开始指向头部
- list<POINT>::iterator i=m_nodeList.begin();
- if(i!=m_nodeList.end()) i++;
- //遍历所有除了第一个节点的节点,看看是不是碰到了自己
- for(;i!=m_nodeList.end();i++)
- {
- //由于i是一个指向POINT类型的迭代器,其行为类似于指向POINT的指针
- //使用i->x是访问i指向的那个POINT的成员x,是(*i).x的简写
- if(i->x == front.x && i->y == front.y)
- return false;
- }
- if(front.x<0 || front.x>=m_rect.right || front.y<0 || front.y >= m_rect.bottom)
- return false;
- return true;
- }
- void ChangeDir(int key)
- {
- switch(key)
- {
- case VK_UP:
- if(m_dir==1||m_dir==3)
- m_dir=0;
- break;
- case VK_RIGHT:
- if(m_dir==0||m_dir==2)
- m_dir=1;
- break;
- case VK_DOWN:
- if(m_dir==1||m_dir==3)
- m_dir = 2;
- break;
- case VK_LEFT:
- if(m_dir==0||m_dir==2)
- m_dir = 3;
- break;
- }
- }
- void Grow()
- {//把自己长大一个
- POINT p=m_nodeList.front();
- //就是给自己的尾巴上添一个节点,添个怎样的节点倒是无所谓
- //上面选择添一个位置在头部的节点为的游戏不出现飞在外面的点
- m_nodeList.push_back(p);
- }
- void Draw()
- {//画自己
- HGDIOBJ hBr=::SelectObject(m_hdc,::GetStockObject(GRAY_BRUSH));
- //这里把迭代器的定义放到了for中
- for(list<POINT>::iterator i=m_nodeList.begin();i!=m_nodeList.end();i++)
- {
- ::Rectangle(m_hdc,i->x,i->y,i->x+width,i->y+height);
- }
- ::SelectObject(m_hdc,hBr);
- }
- POINT GetHead()
- {
- return m_nodeList.front();
- }
- };
- class Seed
- {
- int x;
- int y;
- int m_width,m_height;
- RECT m_rect;
- HDC m_hdc;
- public:
- Seed(int width,int height)
- :m_width(width),m_height(height)
- {}
- void Init(HWND hWnd)
- {
- ::GetWindowRect(hWnd,&m_rect);
- int rx=rand()%(m_rect.right-m_rect.left) / width * width;
- int ry=rand()%(m_rect.bottom-m_rect.top) / height * width;
- x=rx;
- y=ry;
- m_hdc=::GetDC(hWnd);
- }
- void Regain()
- {//被吃到后用来重新产生一个位置
- int rx=rand()%(m_rect.right-m_rect.left) / width * width;
- int ry=rand()%(m_rect.bottom-m_rect.top) / height * width;
- x=rx;
- y=ry;
- }
- void Draw()
- {
- HGDIOBJ hBr=::SelectObject(m_hdc,::GetStockObject(GRAY_BRUSH));
- ::Rectangle(m_hdc, x,y,x+m_width,y+m_height);
- ::SelectObject(m_hdc,hBr);
- }
- POINT GetPosition()
- {
- POINT p;
- p.x=x;
- p.y=y;
- return p;
- }
- };
- 定义一个蛇,一个种子
- Snake snake(width,height,0);
- Seed seed(width,height);
- void Main(HWND hWnd)
- {
- srand(::time(NULL));
- //初始化他们
- snake.Init(hWnd);
- seed.Init(hWnd);
- }
- //^_^,这个就是没有精力去讲的东西
- //这个是运算符重载的例子
- //定义了这个POINT类型就可以用==来判断是不是相等了:)
- bool operator==(const POINT& p1,const POINT& p2)
- {
- return p1.x==p2.x && p1.y==p2.y;
- }
- void Render(HWND hWnd)
- {
- int k=GetKey();
- //根据key的值判断是不是要变向
- snake.ChangeDir(k);
- HDC hDC=::GetDC(hWnd);
- if(!snake.Move())
- {
- ::TextOut(hDC,100,100,"Die",3);
- ::WaitKey();
- ::Exit(hWnd);
- };
- //碰到了豆子
- if(snake.GetHead()==seed.GetPosition())
- {
- //蛇长一截,豆子重新产生
- snake.Grow();
- seed.Regain();
- }
- //画出来
- snake.Draw();
- seed.Draw();
- ::Sleep(100);
- }
复制代码
四、Q&A
1. 什么是结构?
结构是一种符合变量类型,他的地位和int,double等标准类型是相似的,但是他有自己的成员,或者叫域,或者叫字段(foxbase的遗老)。一个结构可以包含各种成员,他们共同组成一种类型。比如可以定一一个结构叫做“人”,包含有成员:名字,年龄,身高等
详情请参见任意一种有素质的语言的教程
2. 数组和链表有什么区别?
数组是内存中连续存放的一组值,可以通过下标直接访问。而链表则是通过指针讲每个节点和下一个节点联系在一起的结构,必须从头节点开始,一个挨着一个的访问,无法通过下标直接找到某个位置的值,他们在内存中存放的位置一般是不连续的 |
|