在日常学习、工作或生活中,大家总少不了接触作文或者范文吧,通过文章可以把我们那些零零散散的思想,聚集在一块。写范文的时候需要注意什么呢?有哪些格式需要注意呢?接下来小编就给大家介绍一下优秀的范文该怎么写,我们一起来看一看吧。
c++24点游戏代码 24点游戏百科篇一
二、选择题(每小题2分,共20分)
1.假设已经有定义char *const pname=”student”;下面的语句正确的。
a.pname[3]=’t’;=”score”;
=new char[5];=new char(‘p’);
2.在下面有关对构造函数的描述中,正确的a.构造函数可以带有返回值
b.构造函数的名字与类名完全相同
c.构造函数必须带有参数
4.构造函数必须定义,不能缺省
3.在下面有关析构函数特征的描述中,错误的。
a.一个类中只能定义一个析构函数
b.析构函数名与类名完全相同
c.析构函数不能指定返回类型
d.析构函数不能带有参数
4.假定ab为一个类,则执行“aba(4), b[3] , * p[2];”语句时,自动调用该类构造函数的次数为。
a.3b.4c.6d.9
5.假定要对类ab定义加号操作符重载成员函数,实现两个ab类对象的加法,并返回相加结果,则该成员函数的声明语句为。
operator+(ab&a,ab&b)ator+(ab&a)
or+(aba)& operator+()
6.假设定义了函数模版:
template
t max(t x, t y){return(x>y)?x:y;}并定义了:int i;char c;那么下列错误的调用该模版的是。
a.max(i,i);b.max(c,c);
c.max(int(c),i);d.max(c,i);
7.模版的主要用途是为。
a.提高代码的运行效率
b.实现多态性
c.提高代码的重用性
d.加强类的封装性
8.下列说法错误的。
a.申明为指向基类对象的指针可以指向它的公有派生的对象,但不允许指向它的私有派生的对象
b.允许将一个声明为指向基类的指针指向其公有派生类的对象,但是不能将一个声明为指向派生类对象的指针指向其基类的一个对象。
c.声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类中定义的成员
d.以上说法都不对
9.现有程序如下:
#include
#include void main(){int kk=1234;
cout<
cout<
}
以上程序的运行结果是。
a.1234b.**1234c.**1234d.以上都不对
12341234**123
410.在公有继承的情况下,基类成员在派生类中的访问权
a.受限制b.保持不变c.受保护d.不受保护
1.1.三、填空题(每空1分,共10分)假定类ab中有一个公有属性的静态数据成员bb,在类外不通过对象名访问该成员bb的写法为(1)。
通常情况下,当创建派生类对象时,首先执行(2)的构造函数,随后再执行(3)的构造函数,当撤销派生类对象时,则先执行(4)的析构函数,随后再执行(5)的析构函数。
ios类中有几个成员函数可以用来对输入输出进行格式化控制,格式控
制主要是通过对(6)的操作来完成的。
所谓赋值兼容规则是指在需要基类对象的任何地方都可以使
用(7)来替代。
纯虚函数是一个在基类中说明却没定义的虚函数,但要求在它的派生类
中必须定义自己的版本,或重新说明为纯虚函数。那么,纯虚函数的定义形式为(8)。2.3.4.5.6.分别写出类ab的单目运算符”++”的前缀方式和后缀方式的友元函数重载一般形式:(9)、(10)。
2.四、写出下面程序的运行结果(每小题5分,共20分)
1、#include
class myclass{public:
myclass(){}
myclass(int a, int b, int c);
void getnumber();
void getsum();
private:
int x,y,z;
static int sum;
};
int myclass::sum=0;
myclass::myclass(int a, int b, int c){
x=a;y=b;z=c;
sum+=x+y+z;
}
void myclass::getnumber(){
cout<<“number=”<
}
void myclass::getsum(){
cout<<“sum=”<
}
void main(){
myclass m(3,7,10);
ber();
();
myclass n(14,9,11);
ber();
();
();
}
2、#include
class tdate1{public:
tdate1(int y,int m, int d);
~tdate1();
void print();
private:
int year, month,day;
};
tdate1::tdate1(int y,int m,int d){
year=y;
month=m;
day=d;
cout<<“contructor called.n”;
}
tdate1::~tdate1(){
cout<<“destructor called.n”;
}
void tdate1::print(){
cout<
}
void main(){
tdate1 today(2005,7,1),tomorrow(2005,7,2);
cout<<“today is ”;
();
cout<<“tomorrow is ”;
();
}
3、#include
class date{public:
date(){
cout<<“date called.”<
}
private:
unsigned int year, month, day;
};
class employee{
public:
employee(){
cout<<“employee called.”<
}
private:
date birthday;
};
class manager: public employee{
public:
manager(){
cout<<“manager called.”<
}
private:
unsigned int allowance;
};
int main(){
manager susan;
return 0;
}
4、#include
class complex{public:
complex(int r,int i):real(r),imag(i){}
friend complex operator++(complex);
friend complex operator--(complex &);
void print(){
cout<<“(”<
}
private:
int real,imag;
};
complex operator++(complex o){
++;
++;
return o;
}
complex operator--(complex &o){
--;
--;
return o;
}
void main(){
complex obj(10,20),obj_b(30,40);
++obj;
();
--obj_b;
();
}
1.五、编程题(每小题10分,共30分)
1、构建一个类book,其中含有两个私有数据成员qu和price,建立一个有5个元素的对象数组,将qu初始化为1~5,将price初始化为qu的10倍。显示每个对象的qu*price。
2、编写一个程序,用成员函数重载运算符“+”和“-”将两个二维数组相加和相减,要求第一个二维数组由构造函数设置,另一个二维数组的值由键盘输入。
3、写一个函数模板,求数组中的最大元素。使得函数调用时,数组类型和返回类型可以是整数也可以是双精度类型。
1.一、简答题(每小题5分,共20分)
1.答:类就是相同的数据和相同的一组对象的集合,即,类是对具有相同数据结构和相同操作的一类对象的描述;对象是描述其属性的数据以及对这些数据施加的一组操作封装在一起构成的统一体,对象可以认为是:数据+操作;类和对象之间的关系是抽象和具体的关系。
2.答:构造函数主要用于为对象分配空间,进行初始化。构造函数除了具有一般成员函数的特征外,还具有一些特殊的性质:(1)构造函数的名字必须与类名相同(2)构造函数可以有任意类型的参数,但不能指定返回类型。它有隐含的返回值,该值由系统内部使用。(3)构造函数是特殊的成员函数,函数体可写在类体内,也可写在类体外。(4)构造函数可以重载,即一个类中可以定义多个参数个数或参数类型不同的构造函数。(5)构造函数被声明为公有函数,但它不能像其他成员函数那样被显式地调用,它是在定义对象的同时被调用的。
析构函数也是一种特殊的成员函数。它执行与构造函数相反的操作,通常用于撤消对象时的一些清理任务,如释放分配给对象的内存空间等。析构函数有以下一些特点:(1)析构函数与构造函数名字相同,但它前面必须加一个波浪号(~);(2)析构函数没有参数,也没有返回值,而且不能重载。因此在一个类中只能有一个析构函数;(3)当撤消对象时,编译系统会自动地调用析构函数。
答:略.详见教材p151.4.答:略.详见教材p192.二、选择题(每小题2分,共20分)
1~10.abbbbccdbb
1.三、填空题(每空1分,共10分)
(1)ab::bb(2)基类(3)派生类(4)派生类(5)基类(6)状态标志字(7)公有派生类对象(8)virtual 函数类型 函数名(参数表)=0;(9)ab operator++();(10)ab operator++(int);
1.四、写出下面程序的运行结果(每小题5分,共20分)
1.number=3,7,10
sum=20
number=14,9,11
sum=54
sum=54
2.contructor ctor is 2005.7.1
tomorrow is 2005.7.2
destructor ctor called.3.date ee r called.4.(10,20)
(29,39)
c++24点游戏代码 24点游戏百科篇二
1类和对象之间的关系
类是对某一类对象的抽象;对象是某一种类的实例、类的定义方法
class类名{ private:私有数据成员和成员函数public:公有数据成员和成员函数 };
类中成员的访问权限
对于public类成员来说,他们是共有的,能被外面的程序访问:@对于private类成员来说,他们是私有的,只能被类中的函数所使用,不能被外面的程序所访问。对象成员的访问方法 访问对象的成员变量,@成员函数和访问变量和函数的方法是一样的,只不过要在成员前面加上对象名和成员运算符“.”表示方式如下
<对象名>.<成员变量><对象名>.<成员函数>(<参数表>)运作有关,如创建窗口,绘制窗口,销毁窗口,通常,消息是从系统发到窗口,或从窗口发到系统。
命令消息:命令消息通常与处理用户请求有关,当用户单击一个菜单或工具栏时,命令消息就产生了。并发送到能处理该消息的类或函数。
控件消息:由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口通(通常是对话框)通知事件的发生。
6.使用一个对话框的一般过程: ①添加对话框资源; ②设置对话框的属性; ③添加和布局控件; ④创建对话框类; ⑤添加对话框代码; ⑥在程序中调用对话框。7模式对话框和无模式对话框
1.什么是类?类的定义格式?类的成员一般分为哪两部分?它们的区别如何?
⑴“类”是对具有共同的属性特征和行为特征的对象的概括和抽象。
⑶类的成员一般分为数据成员和成员函数两部分。
⑷数据成员一般用来表示对象属性特征,而成员函数一般表示了对象的可以进行的操作,即行为特征。
3.什么是对象?如何定义一个对象?对象的成员如何表示?
⑴对象是类的实例,是类的变量。⑵对象的定义:类名对象名称;或:类名 * 对象指针;⑶对象成员的表示:对象名称.成员名字;或: 对象指针→成员名字;9.如何对对象进行初始化?
对象的初始化都是通过构造函数进行的,只要在声明对象时,逐个添好构造函数的实运算符重载。(2)运行时的多态性:虚函数。即在程序执行之前,根据函数和参数无法确定应该调用哪个函数,必须在程序的执行过程中,根据执行情况动态的确定。第3章mfc基本应用程序的建立4.用appwizard创建一个多文档应用程序项目(mdi)ex_mdihello,比较mdi与sdi有哪些区别?
sdi应用程序由应用程序类(cwinapp)、框架窗口类(cframewnd)、文档类(cdocument)、视图类(cview)和文档模板类(csinsledoctemplate)共同作用。mdi应用程序与sdi应用程序的主要差别在于:mdi有cmdiframewnd和cmdichildwnd两个框架窗口类,前一个派生cmainframe类,负责菜单等界面元素的主框架窗口管理;后一个派生cchildframe类,负责相应的文档及其视图的子框架窗口维护。而sdi由框架窗口类cframewnd派生cmainframe类。辑文本的矩形窗口。编辑框属于cedit类的键盘快捷键也称为加速键,加速键一般是几“窗口控件”,可以获得输入焦点。个按键的组合,用于激活特定的命令。12.什么是列表框和组合框?它们的通知消息用ctrl+r进入资源编辑器,选择accelerator有何异同? 的资源项,双击idr_mainframe,进行加列表框控件提供了文本项目的列表供用户选速键资源的添加。择,可以单选也可以多选。列表框是clistbox8.状态栏的作用是什么?状态栏的窗格分为类的;组合框可以看成是编辑框和列表框的几类?如何添加和减少相应的窗格? 组合,它提供了列表框,允许用户从中选择⑴状态栏是一条位于应用程序的主窗口底部项目;也提供了编辑框,允许用户直接输入,的水平条,可以分割成几个窗格,用来显示它是ccombobox类的。应用程序的当前状态信息或提示信息。这两个控件发送的都是通知消息⑵状态栏窗格分为“信息行窗格”和“指示器(wm_notify)。当列表框中发生了某个动窗口”。作,比如双击了列表框中某项时,列表框就⑶要增加一个信息行窗格,则只需在状态栏会向其父窗口发送一条通知消息;而在组合数组中适当的位置增加一个框的通知消息中,有的是列表框发出的,有id_separator标识即可;要增加指示器窗的是编辑框发出的。格,则在状态栏数组的适当位置增加一个在14.什么是滚动条、进展条、滑动条和旋转“字符串表”中定义过的资源id。若要减少窗按钮控件? 格,只需减少数组元素即可。⑴滚动条:是csrollbar类的控件,它有一个9.如何在状态栏的窗格显示文本? 构造函数和析构函数
⑴模式对话框:所谓的模式对话框是指对话构造函数是类的成员函数,功能是在创建对框弹出时,用户必须向对话框进行相应的操象时,实用给定的值将对象初始化。它有以作,在退出对话框之前,其所在的应用程序下特点:
是不能往下执行的。
构造函数的名字必须与类名相同。
⑵无模式对话框:无模式对话框是指对话框构造函数可以有任意的形参,但不能具有返弹出后,一直保留在屏幕上,用户可以在对回值。
话框所在的应用程序中进行其他的操作。定义对象时,编译系统会自动地调用构造函两种对话框在编辑器设计和使用
数。
classwizard进行编程时方法基本一致,但在构造函数可以重载
创建和退出对话框窗口时的方式不同。析构函数也是类的成员函数,功能是用于释在创建时,模式对话框是由系统自动分配内放一个对象,在对象删除前,用它来做一些存空间,因此在对话框退出时,对话框对象内存释放等清理工作,与构造函数功能相自动删除。而无模式对话框则需要用户来指反。,它有以下特点:
定内存,退出时还要自己删除对话框对象。析构函数名也应与类名相同,只是在函数名在退出时,两种对话框所用的终止函数不一前面加一个波浪符~。
样。模式对话框通过调用
析构函数不能带任何参数,也没有返回值。cdialog::enddialog来终止,而无模式对话每个类有而且只能有一个析构函数。析构函框则是通过调用cwnd::destroywindow来终数不能重载。
止。
清理内存空间时,会自动地调用析构函数 8.静态控件的类型单继承和派生类的构造函数与析构函数 静态文本组框静态图片 基类的构造函数和析构函数不能被派生类继9.按钮的分类、消息
承
按钮控件是cbutton类的控件。
派生类对象在建立时,先执行基类构造函数,按键按钮 单选按钮 复选框按钮(复选框的再执行派生类构造函数。析构函数执行顺序外形是在文本前有一个空心方框,当它被选与构造函数相反,先执行派生类析构函数,中时,复选框中就加上一个标记,通常再执行基类的析构函数。复选框只有选中和未选中两种状态,若复选两种多态性
框前面有一个是灰色,则这样的复选框编译时的多态性和运行时的多态性。编译时是三态复选框,它表示复选框的选择状态是的多态性是通过函数的重载或运算符的重载“不确定”。)
来实现的。运行时的多态性是通过虚函数来bn_clicked(单击按钮)
实现的,它是只在程序执行前,根据函数和bn_double-clicked(双击按钮)参数还无法确定应该调用哪一个函数,date函数
在程序的执行过程中,根据执行情况动态地updatedata 函数只有一个true或false的确定。
参数。当调用updatedata(false)时,s的应用程序特点有哪些?由控件相关联的成员变量向控件传输,当调消息驱动机制
用updatedata(true)或不带参数的图形设备接口(gdi)updatedata时,数据由控件向相关联的成员基于资源的程序设计 变量复制。
动态链接库
11、工具栏和菜单项的结合方法
进程和线程 的appwizard(exe)提供了哪几种类型样即可。的应用程序?
12、状态栏的设定方法
(1)单文档界面的应用程序(sdi,single 用mfc appwizard创建的单文档和多文档应document interface);
用程序框架中,有一个静态的indicators数(2)多文档界面的应用程序(mdi,multiple 组,文件中定义的,被documents interface);
mfc用作状态栏的定义(3)基于对话框的应用程序(dialog )。
要增加一个信息行窗格,应用程序项目组织:dsp 项目文件 dsw 组中适当的位置增加一个id_separator标识项目工作区文件 cppc++文件,c++头文件 即可;要增加指示器窗格,则在状态栏数组工作区窗口的标签页面:classview页 的适当位置增加一个在“字符串表”中定义resourceview页和fileview页 过的资源id。若要减少窗格,程序类结构
元素即可。
对话框类caboutdlg 应用程序类2.如何在状态栏的窗格显示文本?(3种)cex_sdihelloapp应用程序文档类1.)调用cwnd::setwindowtext更新信息行cex_sdihellodoc
窗格(或窗格0)中的文本 应用程序视图类cex_sdihelloview2.)手动处理状态栏的cmainframe类(用于负责主框架窗口的显示on_update_commadnd_ui更新消息,并在处理和管理)
函数中调用ccmdui::setpanetext函数。5.消息的分类有哪些:
3.可以调用cstatusbar::setpanetext函数标准(窗口)消息:窗口消息一般与窗口内部更新任何窗格中的文本.(通用)参。
20.派生类是如何定义的?它有哪些特点?⑴定义派生类的一般形式为:
class派生类名:[引用权限] 基类名{派生类新增的数据成员
派生类新增的成员函数};
⑵派生类继承了基类的所有数据成员和成员函数,并增加新的成员。
21.派生类的继承方式有哪些?它们各有哪些特点?
①当引用权限为public时,称为“公用派生类”。在公用派生类中,基类的public成员和protected成员仍然成为派生类中的public成员和protected成员;然而基类中的私有成员却成为了派生类中的“不可访问的成员”,不能被派生类使用。
②当引用权限为private时,称为“私有派生类”。在私有派生类中,基类的公用成员和保护成员成为了派生类中的私有成员,基类的私有成员成为派生类“不可访问的成员”。③从以上两点可以看出,基类中的私有成员在派生类中都成为了“不可访问的成员”。因此在继承中经常使用另外一种封装字protected,被protected封装的成员称为“保护成员”,保护成员和私有成员相似,不能被外界引用,但它却可以被派生类的成员函数引用。
22.在定义派生类的过程中,如何对基类的数据成员进行初始化?
基类的数据成员初始化往往是通过基类的构造函数进行的,但是在么在建立派生类的对象时,是不会自动执行基类的构造函数,因而就会使基类中成员变量未初始化。所以在设计派生类的构造函数时,不仅要考虑派生类所增加的成员变量初始化,还应当考虑基类的成员变量初始化。在执行派生类的构造函数时,应当调用基类的构造函数。
因此考虑到基类成员变量初始化后,派生类的构造函数一般形式为:
派生类构造函数名(参数表):基类构造函数名(参数表)
23.在派生类中能否直接访问基类中的私有成员?在派生类中如何实现访问基类中的私有成员?
在派生类中是不能直接访问基类中的私有成员的,要在派生类中访问基类中的私有成员,只能通过基类所提供的public或protected封装的成员函数进行。
24.什么是虚基类?它的作用如何?
在声明派生类时,在基类的引用权限前面加上virtual,就将基类声明成了虚基类,即:class 派生类名:virtual [引用权限]基类名虚基类的引入主要是针对多重派生的,是为了消除派生类的二义性使用的。通过虚基类派生出来的新类,同一个成员变量在不同的派生类中都只有一个复制,当通过普通基类派生的新类,同一个成员变量,在不同的派生类中的副本是不一样的。
25.多态性:不同类型的对象接收相同的信息时产生不同的行为。例如函数重载。
分类:(1)编译时的多态性:函数重载或第4章对话框及常用控件
1.什么是对话框?它分为哪两类?这两类对话框有哪些不同?
(2)对话框是cdialog类的派生类,它的主要功能是输出信息和接收用户的输入。对话框是一个特殊类型的窗口,任何对窗口的操作都可以在对话框中进行。对话框与控件是密不可分的,在每个对话框内一般都有一些控件,对话框依靠这些控件与用户进行交互。2.什么是对话框模板、对话框资源和对话框类?
⑴对话框模板:mfc develop studio提供的对话框模板是用来创建用户对话框资源,用对话框模板创建的基本界面上,包括一个ok(确定)按钮和一个cancel(取消)按钮等。可以移动、修改、删除这些控件,或者是增加新的控件到对话框模板,构成应用程序所需的对话框资源。
⑵对话框资源:对话框资源是一个用户输入或取得数据的图形界面。这个图形界面是使用
对话框编辑器在对话框模板上创建的,程序员可以在对话框模板上增加并编辑控件,生成对话框资源。当应用程序运行时,就可以得到一个对话框。⑶对话框对象
mfc使用cdialog类来描述对话框,它是cwnd类的派生类。在cwnd类的基础上增加了数据交换的功能。当创建一个新的对话框资源后,使用classwizard可以创建一个对话框类的派生类。对话框对象实现了对话框和应用程序之间的通信。在应用程序中定义一个对话框对象后,在对话框关闭后,可以通过访问对话框对象的成员变量获得用户的输入数据。
4.什么是控件?根据控件的性质可以将控件分为几类?
控件是系统内部定义的能完成特定功能的一些组件,控件能够放置在一个对话框中,提供应用程序与用户交互的某种功能的类,根据控件的特性和功能,一般可以将其分为3类:windows通用控件、activex控件以及mfc新增控件,windows通用控件一般都是从cwnd派生而来的。6.什么是ddv/ddx技术?如何使用这种技术?
为了能方便地操作一个控件,mfc采用了独特的ddx(dynamic data exchange,动态数据交换)和ddv(dynamic data verify,动态数据交换)技术。ddx将数据成员变量与对话类模板内的控件相连接,这样使得数据在控件之间很容易传输。ddv用于数据的校验,它能自动校验数据成员变量数值的范围,并给出警告。
7.什么是控件的通知消息?它在编程中起哪些作用?
当控件状态发生改变时,控件就会向其父窗口发送消息,这就是控件的通知消息。控件的通知消息可以是一条wm_command消息,也可以是一条wm_notify消息。9.什么是编辑框控件?它有哪些功能?
编辑框是一个允许用户从键盘输入数据和编独立的窗口,两端有两个箭头按钮,中间有一个可移动的滚动块,具有直接的输入焦点,分为垂直滚动条和水平滚动条两种类型。⑵进展条:用于说明一个操作的进度,在操作过程中不断地从左到右填充进展条,可以让用户看到还有多少任务要完成。⑶滑动条:由滑动块和可选的刻度线组成的,它是csliderctrl 类的。⑷旋转按钮控件:是cspinbuttonctrl类的,由一对箭头按钮组成,它通常与一个相伴的控件一起使用。15.什么是旋转按钮的“伙伴”控件?如何设置? 与旋转按钮控件一起使用的控件称为“伙伴(buddy)控件”,单击旋转按钮控件的箭头按钮,可以增大或减小其伙伴控件中某个值。伙伴(buddy)控件的设置有两种方法: ①setbuddy:设置旋转控件的伙伴窗口。②在对话框中用ctrl+d进行设置
16.在mfc中,通用对话框有哪些?如何在程序中使用它们?
windows提供了一组标准用户界面对话框,它们都有相应的mfc库中的类来支持,所有这些通用对话框类都是从一个公共的基类ccommondialog派生而来的。
17.如果消息对话框只有“是”和“否”两个按钮,则如何设置messagebox函数的参数?答:可以将messagebox函数中的参数ntype设置为mb_yesno。
消息对话框用于显示信息有两种类型afxmessagebox和messagebox,前者为全程函数,messagebox只能在控件对话框,窗口等一些窗口类中使用
1.菜单有哪些常见的规则?p221
为了使windows程序更容易操作,菜单的显示都遵循下列一些规则:
⑴若单击某菜单项后,将弹出一个对话框,那么在该菜单项文本后有“…”。
⑵若某项菜单有子菜单,那么在该菜单项文本后有。
⑶若菜单项需要助记符,则用括号将带下画线的字母括起来。助记符与alt构成一个组合键,当按住“alt”键不放,再敲击该字母时,对应的菜单项就会被选中。
⑷若某项菜单需要快捷键的支持,则一般将其列在相应菜单项文本之后。
2.什么是助记符?它是如何在菜单中定义的?菜单项的助记符是用括号和带下画线的字母括起来的符号,助记符与alt构成一个组合键。
3.菜单项的消息有哪些?
菜单项产生的消息有:command 消息和update_command_ui消息。
4.若对同一个菜单用class wizard分别在视图类和主框架窗口类cmainframe都处理其 command消息,并在它们的函数中添加相同的代码,则当用户选择该菜单后,会有什么样的结果?为什么?
命令消息处理的优先级别为:命令→视图类→文档类→文档模板类→框架窗口类→应用程序类。因此,视图类的消息处理函数会执行,而
5.什么是键盘快捷键?它是如何定义的?
可以调用cstatusbar::setpanetext函数更新任何窗格中的文本。11.什么是命令更新消息?它的作用是什么? update_command_ui消息又称为“更新命令ui消息”,该消息映射函数中有一个ccmdui类的对象pcmdui,ccmdui类中包含了一个指向菜单项或工具栏按钮的指针,于是利用该指针对菜单项或工具栏按钮的显示形式进修正
c++24点游戏代码 24点游戏百科篇三
c++24点游戏
#include “iostream” #include “string” using namespace std;
//定义stack类
const maxsize=20;
enum error_code { success, overflow, underflow };
template
class stack { public: stack();
bool empty()const;bool full()const;int size()const;void clear();error_code top(t &item)const;error_code pop();
error_code push(const t &item);private: int count;t entry[maxsize];};
template
stack
::stack(){ count=0;}
template bool stack::empty()const { return count==0;}
template bool stack::full()const { return count==maxsize;}
templateint stack
::size()const { return count;}
templatevoid stack
::clear(){ count=0;}
template error_code stack::top(t &item)const { if(empty())return underflow;item= entry[count-1];return success;}
template error_code stack::pop(){ if(empty())return underflow;count--;return success;}
template error_code stack::push(const t &item){ if(full())return overflow;entry[count++]=item;return success;}
stacksign;stack
num;
int set;// 判断程序中的异常,以便适时退出?//
void process(char c)
//计算两个数的 +-* / 运算// { int k=0;double a,b;();
if((b)==success){
();
if((a)==success){ ();k=1;} } if(k){ switch(c){
case '+': (a+b);break;case '-': (a-b);break;case '*': (a*b);break;case '/': if(b==0){ set=4;(-1);} else
(a/b);break;} }
else {set=1;(-1);} }
void get_command(string &str){
cout<<“n请输入要进行运算的表达式,包括” +,-,*,/,=,(,)“和数字,”<
<<“注意: 以数字开头,等号结尾,中间括号要匹配.”<
>str;}
double do_command(const string &str){ string s=“";double outcome=-1;char c;
for(int i=0;str[i]!='';i++){
if(set!=0)break;//例外 则停止运行
while(1){ //分离数据与运算符
if(str[i]<='9' && str[i]>='0' || str[i]=='.'){ s+=str[i];i++;} else { if(s!=”“){
if((atof(s.c_str()))==overflow)set=3;s=”“;} break;} }
char ch= str[i];
switch(ch){ //处理运算的优先级,并注意例外抛出
case '*': case '/':
if((c)==success)if(c=='*'||c=='/')process(c);if((ch)==overflow)set=3;break;case '+': case '-':
while((c)==success){ if(c!='(')process(c);else break;}
if((ch)==overflow)set=3;break;case '(':
if((ch)==overflow)set=3;break;case ')':
while((c)==success){ if(c!='(')process(c);else break;}
();break;case '=':
while((c)==success){ if(c!='(')process(c);else break;} break;
default: set=2;break;} }
if(()==1 && ()==0)(outcome);else set=1;
if(set==0)cout<<”运算结果是:n“<
if(set==1)cout<<”n您输入的不匹配,有错误发生。result lost!“<
if(set==4)cout<<”n 分母为0,不能进行除法运算,出现溢出,lost result!“<
return outcome;}
void main()int f(){ double out;do { string str,s;set=0;
get_command(str);s=str;
if(str[0]=='-')str='0'+str;//处理表达式中的负号
for(int i=1;str[i]!='';i++){ if(str[i]=='-' && str[i-1]=='('){ (i,”0“);i++;} } out= do_command(str);
cout<
我gai过此行
cout<<”如果你算的结果不等于24,需要重新计算请输入你算出的结果,程序有漏洞,请合作。“<
>out;cout<<”谢谢合作“<
return out;}
int main(void)
{
int ii,zz,jj;
printf(”0-12的4个数字nn“);
for(jj=0;jj<2;)
{
for(ii=0;ii<4;ii++)
{
zz=rand()()% 12;
cout<
}
cout<<”请用这4个数字算出24“<
f();
cout<<”是否继续;是1否2"<
cin>>jj;
cout<
}
c++24点游戏代码 24点游戏百科篇四
第3章 “速算24”扑克游戏--单元、异常、逻辑
3.1 “速算24”扑克游戏效果说明
“速算24”是一个考察心算能力、有助于开发智力的扑克游戏。在给出4张扑克牌之后,要求应用这些扑克牌数字做数学运算,迅速构造出一个数学表达式,得出结果24。这个游戏的关键在于迅速判断用户输入的表达式是否正确,并做出相应的反馈,告诉用户是算对了还是算错了。游戏的初始界面如图3.1所示。
图3.1 游戏的初始界面
当用户单击“开始”按钮时,系统开始发牌,随机发出4张牌,如图3.2所示为随机开始的一局游戏,给出的4张纸牌分别为“9”,“8”,“9”,“2”。在文本框中输入运算表达式,比如,输入“8*(2+(9-9))”,单击“计算”按钮,系统会出现提示框,显示“您输入的表达式的计算结果为16!”,告诉你该表达式的结果不是“24”,如图3.3所示。单击“确定”按钮,再次在文本框中输入表达式,比如“8*(2+(9/9))”,单击“计算”按钮,系统会出现提示框,显示“您真行,我服了您!”,表明运算正确,如图3.4所示。
图3.2 系统随机发4张纸牌
图3.3 运算式不正确
图3.4 运算式正确
这个游戏具体的规则如下:
(1)单击“开始”按钮,游戏开始,系统将随机发牌。
(2)请迅速在文本框中输入运算表达式,然后单击“计算”按钮。
(3)这时系统会提示您的运算是对了还是错了,在弹出的对话框中单击“ok”按钮,再次输入新的运算表达式,重复上一步,直到您的运算表达式结果正确,这时系统会恭喜您!
(4)如果结果错了还想继续或者中途想计算另一局扑克牌,就单击“重新开始”按钮,得到新一局扑克牌进行游戏。
下面,我们开始循序渐进地创建这个小游戏。在最开始,游戏的界面和效果都会非常简单,在后面我们会逐渐地完善它。
第3章 “速算24”扑克游戏--单元、异常、逻辑
3.2 生成和建立程序(1)在程序中用到了image组件,用于放置图片。还用到了timer组件,用于计算用户操作时间。下面我们来生成游戏的基本框架。
3.2.1 image组件
image组件在additional页上,用来在窗口中显示一幅图片。它拥有如下几个主要属性: e属性
可以在picture属性中调入图像文件。delphi支持多种图像格式,如位图(.bmp)、图标(.ico)、图元(.wfm)、动画光标(.ani)、jpeg图片(.jpg、.jpeg)等。
ze属性
当autosize为true时,image组件将根据它所包含的图像的大小来调整自身的大小;当autosize为false时,不论图像有多大,组件将保持设计时的大小。如果组件比图像小,那么只有一部分图像是可见的。
h属性
当stretch为true时,位图图像将根据组件的大小调整自身的大小,当组件大小改变时,上述三种文件也做相应变化。stretch属性对图标没有作用。
上述的autosize和stretch属性决定了图像在窗口中的显示尺寸。
图3.5演示的3个image分别为:autosize为true,autosize为false,stretch为true的情形。可以看到,image的原始尺寸比图片宽,矮,在上面的属性设置下,就会有不同的显示效果。
图3.5 autosize和stretch的设置 3.2.2 timer组件
在delphi中,组件分可视组件和非可视组件。可视组件是指那些在运行期间仍然能显示的组件,例如label,button,image组件等。非可视组件是指那些在程序界面设计期间可见,而在程序运行时不可见的组件,例如在system页上的timer组件。
timer组件能够有规律地触发ontimer事件,发送信息给应用程序,它是编制应用程序时最为重要的组件之一。
组件的属性
enabled属性表示timer是打开还是关闭。用interval属性设置两个ontimer事件间的间隔,单位是毫秒。将间隔设置为0相当于关闭计时器,interval是cardinal类型的,最大值可到4294967295,当然程序中一般不会把interval设成很大的值。组件的使用
timer是独立的对象,在启动与windows无关的逻辑和应用事件时极其有用。可以模拟时钟或计时器,可视地显示经过的时间;可以用作系统延时,delphi提示信息出现只需在该区域停顿几秒,就是timer组件应用的一个例子;可以检查系统环境、事件,根据结果进行响应;也可以在窗口中闪烁一段正文或图像,提示某种操作或处理正在进行等等。
尽管delphi的计时器每秒可以产生1000次激发,在编程中还必须注意程序对timer触发的响应。如果程序处理ontimer事件的时间超过interval的设定值,就可能错过事件,因为当下一次触发到来时,系统正忙于处理上一事件,则这次触发就会被忽略。同时要注意其他的windows应用程序是否会影响timer的触发。如果后台正运行着一个占用处理器的程序,就可能会导致timer的触发不准确,从而使前台程序运行出现错误。
这里要强调的是timer组件是一个非可视组件,可以把它放置到窗体或者其他容器组件上的任何位置。3.实现游戏计时功能
在本章的游戏中,我们加入一个timer组件,实现游戏的计时功能。
在窗体中加入一个label组件,将此组件的caption属性设置为“使用时间”,然后从组件面板上选择system页中的timer组件。
在unit1中加入form1的一个私有成员spendtime,记录用户计算所用的时间。代码如下所示:
private { private declarations } spendtime:integer;在form1的oncreate事件中加入如下代码。将spendtime设置为0,并将timer1的enabled属性设置为false,使timer1组件不能响应ontimer事件;并将timer1的interval属性设置为1000,表示当timer1有效时,每间隔1000ms(即1秒)发生一次ontimer事件:
procedure eate(sender: tobject);var i:integer;begin //初始化,设置数组randomdata的长度为4 //并将每个数组元素初始化为零
setlength(randomdata,4);for i := 0 to 3 do randomdata[i]:=0;spendtime:=0;d:=false;al:=1000;end;然后在标题为“开始”的“开始”按钮的onclick事件中,加入如下所示的代码,将timer1的enabled属性设置为true,使timer1组件有效,即现在timer1能响应ontimer事件,计时开始。并将spendtime重新设置为0:
d:=true;al:=1000;spendtime:=0;//将spendtime重新设为0 再在“计算”按钮的onclick事件句柄中,增加下面的语句,使timer1无效:
d:=false;最后双击timer1组件,创建timer1的ontimer事件句柄,在其中加入如下所示的代码,将spendtime加1,并设置label5的caption属性:
procedure tform1timer(sender: tobject);begin spendtime:=spendtime+1;n:='使用时间:'+inttostr(spendtime)+'秒';end;这样,每隔1秒钟,程序就刷新一次使用时间。
3.2.3 设计初始界面
按住shift键,然后单击组件面板中additional页中的image组件,这时该组件边缘出现蓝色的边框,并且凹陷下去,表示可以在窗体上连续加入几个image组件。选择好image组件后,在窗体的左上角单击,加入1个image组件,然后依次向右单击鼠标3次,再加入3个image组件。最后再单击组件面板中最左边的箭头。
为了排列这4个image组件,先利用shift键将它们同时选上,然后右击,选择align命令,在随后出现的alignment对话框中的horizontal选项组中选择space equally,在vertical选项组中选择tops,表示这4个组件顶端对齐,水平方向上间距相等。
按照同样的方法加入4个label组件、3个button组件和1个edit组件。按照表3.1所示设置各个组件的属性。
表3.1 各个组件的属性
组件名 form1 label1 属性名 caption caption autosize wordwrap caption
属性值 速算24
1.单击“开始”按钮,游戏开始,系统将发出4张扑克牌
false false
2.要求用户利用扑克牌显示的数字,通过加减乘除运算,以最快的速度得出24(可以使用括号),jqka和“王”算做1。然后在文本框中写好表达式,接
着单击“计算”按钮
label2
label3 autosize wordwrap caption
false true
3.这时系统会计算输入表达式的结果,告诉用户是对还是错了。在弹出的对话框中单击“ok”按钮,如果错了可以再次输入新的表达式,重复上一步。直
到您的表达式正确,这时系统会恭喜算对了!
label4 autosize wordwrap caption autosize caption caption caption text
false true 在下面输入数学表达式
false 开始 计算 退出游戏 空
button1 button2 button3 edit1
现在同时选择label1,label2和label3,将它们左对齐,垂直方向等距离排列。将button1,button2和button3左对齐,垂直方向等距离排列。
下面放置4个image组件,用于放置4张纸牌的图片。
先选择image1,然后切换到对象查看器中的属性编辑器,选择属性选项页中的picture属性,然后在picture属性值栏中双击,或单击此属性值旁边的带有省略号的按钮,打开picture editor对话框,如图3.6所示。然后单击load按钮,弹出load picture对话框,文件。最后单击ok按钮,退出picture editor对话框。
图3.6 指定图片
使用同样的方法,设定其他3个image组件的picture属性。
保存我们的新项目,运行之后界面如图3.7所示,与运行时的界面图3.1稍有不同。这里是设计时界面,只有界面没有事件响应。是程序创建过程中的一个步骤的检验。但是,这个程序还没有什么具体的功能,为了让游戏运行起来,必须添加代码,创建相应的事件处理程序。
第3章 “速算24”扑克游戏--单元、异常、逻辑
生成和建立程序
3.2.4 事件处理
需要添加个事件:第一个用于响应单击开始按钮,在此事件中完成发牌,即随机显示图片;第二个用于响应单击计算按钮,解析用户在文本框中输入的表达式,计算表达式的结果,并判断表达式的结果是否等于;第三个用于响应单击退出游戏按钮,退出游戏(程
序)。
1.数据初始化
创建窗体的事件处理程序,在这里进行必要的初始化。第一步先在unit1中添加form1的私有成员数组:
private
{ private declarations }
randomdata:array of integer;然后,在对象查看器中选中form1,选中event选项卡,在oncreate一栏对应的右边的空白栏中双击,创建oncreate函数。添加如下代码。
procedure eate(sender: tobject);
var
i:integer;
begin //初始化,设置数组randomdata的长度为4
//并将每个数组元素初始化为零
setlength(randomdata,4);
for i := 0 to 3 do
randomdata[i]:=0;
end;这里使用一个for循环语句,i是循环变量,格式是:for循环变量:=初值to末值do循环体。你也可以借助delphi的自动完成功能,在输入for之后按下ctrl+j键,生成如下代码:
procedure eate(sender: tobject);
begin
for := to do
begin end;
end;在上述代码中,程序首先利用setlength函数设定可变数组randomdata的数组长度为4,然后,将数组的每一个单元都设置为0。这样,就完成了数组的数据初始化工作。
2.“开始”按钮的click事件处理
()功能单击开始按钮时,系统就随机地发出张纸牌,显示在个组件中。
()代码首先,我们需要一个循环变量,一个字符串变量存放随机选取的图片的文件名。创建“开始”按钮的onclick事件处理程序,在begin前头添加需要的变量,然后在此事件
中加入如下所示的代码。
procedure 1click(sender: tobject);
var
i:integer;
filename:string;
begin
randomize;//初始化随机数
for i := 0 to 3 do
begin randomdata[i]:=random(13)+1;//产生一个1到13的随机数
filename:=inttostr(randomdata[i])+'.bmp';//根据随机数,得到文件名
//根据i的不同为不同的image组件载入图像文件
case i of 0 : omfile(filename);1 : omfile(filename);2 : omfile(filename);3 : omfile(filename);
end;
:='';
us;
end;
end;在delphi内部,随机数的产生实际上也是在一套算法的控制之下,randomize函数用于初始化产生随机数的种子,保证两次产生的随机数不同,而random(i:integer)则利用初始化过后的随机数种子产生一个1~i之间的随机数。这里i设定为13,以配合扑克牌的张数13。
用来将一个整数转换成一个字符串,和上一章中的的功能恰好相反。我们已经预先准备了位图文件,它们都是形式,文件名则是利用数字命名。中特殊的字符串运算符将两个字符串串联起来,产生一个新的字符串,我们需要的位图文件后缀是,因此,在产生文件名的时候,在数字后面加上这个字符串。语句在上一章中已经讲过,单击开始按钮后,准备接受用户的输入,然后利用方法,把焦点设置到上。是提供的方法之一,用于设置输入
焦点到某个指定的组件。
3.“计算”按钮的onclick事件
双击“计算”按钮,创建此组件的onclick事件响应句柄,然后在代码编辑器中加入如下所
示的代码,用于计算用户输入的表达式。
procedure 2click(sender: tobject);
var
result:integer;
answer:string;
begin
result:=totalcompute();
if(result=24)then ebox('您真行,我服了您!','对了',mb_ok)
else
begin answer:='您输入的表达式的计算结果为'+inttostr(result)+'!';ebox(pchar(answer),'错了',mb_ok);
end;
end;这段程序根据自定义函数的计算结果判断用户的输入正确与否,并且输出相应的结果以提示
用户。
在语句“result:=totalcompute()”中用到了一个自定义的函数totalcomp-ute,我们用它来计算用户输入的表达式。现在我们还没编写这个函数的代码,所以,目前这段代码是无法运行的。没关系,可以先把它用“//”注释掉,然后随便给 result赋一个值,测试这个事件处理程序的其他部分是否运行正常,例如:
//result:=totalcompute();
result:=24;这样,运行后,单击“计算”按钮后的显示如图3.8所示。
图3.8 成功的提示信息
我们用一个类型的变量存放出错信息,但是函数的第一个参数要求是(字符指针)类型,因此,我们用一个强制类型转换将转换
成。4.“退出游戏”按钮的onclick事件
双击“退出游戏”按钮,创建此组件的onclick事件处理程序,然后在代码编辑器中加入如
下所示的代码,用于退出程序。
procedure 3click(sender: tobject);
begin
close;
end;在上一章中我们提到可以用代替,但这里使用来结束程序的运行。这样,可以通过事件来指定在什么条件下窗体可以关闭。
第3章 “速算24”扑克游戏--单元、异常、逻辑
生成和建立程序equery事件
当调用方法来关闭窗体时,事件发生。利用事件来指定在什么条件下窗体可以关闭。事件包含一布尔型的参量,可以用它来决定窗体是否关闭。的默认值为。可以利用事件来询问用户是否真的希望马上关闭窗体。我们在这里弹出一个对话框,代码如下所示:
procedure osequery(sender: tobject;var canclose: boolean);begin if(messagedlg('现在要退出游戏吗?', mtconfirmation, [mbok, mbcancel], 0)= mrok)then canclose:=true else canclose:=false;end;messagedlg是一种提示对话框,第一个参数是对话框询问的讯息,是一个字符串;第二个参数则代表对话框的类型,mtconfirmation是一个tmsgdlgtype的枚举类型,表示这个对话框是个确认对话框。tmsgdlgtype类型如下所示:
type tmsgdlgtype =(mtwarning, mterror, mtinformation, mtconfirmation, mtcustom);以上定义的对话框类型分别表示:警告、错误、提示、确认和自定义类型。
第三个参数是tmsgdlgbtn类型的集合,这个集合包含了类型为tmsgdlgbtn的按钮,tmsgdlgbtn的定义如下:
type tmsgdlgbtn =(mbyes, mbno, mbok, mbcancel, mbabort, mbretry, mbignore, mball, mnnotoall, mbyestoall, mbhelp);在我们的程序中,利用了一个集合[mbok, mbcancle],表示在对话框中显示两个按钮:ok和cancel的组合。
最后一个参数是用来描述帮助索引的长整型变量,用来获取提示用户的帮助信息,这里我们没有任何帮助信息,先不管它。函数和用户交互,返回一个类型的数字,预先定义了一套数字,用来直观地表示对话框返回信息。如果用户单击了按钮,这个对话框返回,效果如图所示。
图3.9 关闭窗口时询问用户
现在,我们已经完成了程序的主体部分。第3章 “速算24”扑克游戏--单元、异常、逻辑
单元间的互相引用3.3.1 单元引用的概念
在第章关于单元的知识里,我们已经知道可以定义不包含窗体的单元,它集中定义了程序中使用的函数,这一节里,我们就要实现这样的一个单元,用来实现上面提到的函数。我们可以在任何单元中编写自己的函数,当然包括与窗体相连的单元。可是我们常常需要用到一些常用的函数,这时最好是创建一个不与窗体相连的独立单元,用它来容纳自己的函数,这称为独立的单元文件。当创建了不与窗体相连的独立单元文件后,项目中的其他单元就能很容易地共享这些函数,其他项目也可以很容易地调用这些函数了。对于单元间的引用,要用到语句。3.3.2 uses语句
语句告诉程序在最终的执行代码中需要用到哪些函数和过程。会自动把一些必须的单元包括进来,例如,,等。对于我们自己编写的单元,如果程序中使用了该单元的函数或代码,也需要包括在部分中。语句具有两种类型:公有引用和私有引用。在部分包含的语句代表的是本单元的公有引用,就是说,这部分的引用可以被其他引用本单元的单元继承性地引用。在部分的语句应包含在部分中的代码所需要的单元,去掉那些可以自动加入到程序中的单元。在部分包含的语句代表的是本单元的私有引用,就是说,这部分的引用只能被本单元内部使用。在部分的语句应只包含在部分中的代码所需的单元的名字。对于单元间的引用,要避免交叉引用。假设有两个单元和,如果出现在的部分的语句中,那么单元便不能出现在单元的的语句中。因为这样会产生对单元的循环访问,编译时会出现错误信息。3.3.3 创建另一个单元
创建一个不与窗体相连的单元文件的方法是,首先选择主菜单的命令,然后选择命令,此时弹出一个对话框,如图所示。在此图中选择选项卡中的,然后单击按钮。此时自动为我们创建一个名为的独立单元文件,并显示在代码编辑器中,我们只需在此加入函数即可。
图3.10 new items对话框
单元创建之后,就需要实现单元之间的互相引用。这里有两种方法:(1)直接在unit1中写入uses unit2,代码如下所示:
var form1: tform1;implementation uses unit2;(2)选择主菜单的file | use unit命令,此时delphi弹出use unit对话框,如图3.11所示,在此窗口中列出当前文件没有连接的所有文件,只需选择需要连接的文件即可。当选择了某一文件并单击ok按钮后,当前文件就包含了对所选文件的引用。
图3.11 use unit对话框
如果当前文件已经连接了当前项目中所有其他文件,选择命令后,就会弹出如图所示的信息窗口,告诉程序员当前文件已经连接了当前项目中所有其他文件。
图3.12 information对话框
此时再编译,程序就没有任何错误了。现在我们已经创建了,它将用作我们的数学函数定义单元。在开始定义这个单元之前,需要先了解一下关于的异常处理机制。第3章 “速算24”扑克游戏--单元、异常、逻辑
3.4 异 常 处 理
3.4.1 异常处理的概念 在应用程序开发中如何检测、处理程序的运行错误是一个很重要的问题。在 delphi 的ide(集成开发环境)中提供了一个完善的内置调试器,可以发现大部分程序错误。但并不是所有的错误都可以被发现,而且当程序涉及到与外设的数据交换或操作外设,如要求用户输入、读写磁盘等时,错误的发生是程序无法控制的,如输入非法字符、磁盘不能读写等。这些情况不仅会导致应用程序异常中止,而且可能引起系统的崩溃。针对这些问题,delphi提供了一套强大的异常处理机制。巧妙地利用它,可以使程序更为强健,使用更为友好。
delphi异常处理机制建立在protected blocks(保护块)的概念上。所谓保护块是指用保留字try和end封装的一段代码。保护块的作用是当应用程序发生错误时自动创建一个相应的exception(“异常”类)。程序可以捕获并处理这个“异常”类,以确保程序的正常结束以及资源的释放和数据不受破坏。如果程序不进行处理,则系统会自动提供一个消息框。“异常”类是delphi异常处理机制的核心,也是delphi异常处理的主要特色。delphi提供的所有“异常”类都是类exception的子类。用户也可以从类exception派生一个自定义的“异常”类。
3.4.2 资源保护方式
回收分配的资源是确保程序健壮性的一个关键。但默认情况下异常发生时程序会在出错点自动退出当前模块,因此需要一种特殊的机制来确保即使在异常发生的情况下,释放资源的语句仍能被执行,而delphi的异常处理正提供了这种机制。
delphi提供了一个保留字finally,用于实现资源的保护。
{分配资源}
try {资源使用情况} finally {释放资源}
end;try„finally„end就形成了一个资源保护块。finally后面的语句在任何情况下(不论程序是否发生异常)都会执行。
在异常保护的情况下,当异常发生时,系统会自动弹出一个消息框,在框中显示异常的消息。退出当前模块后异常类自动清除。
3.4.3 异常响应方式
异常响应为开发者提供了一个按需进行异常处理的机制。try„except„end形成了一个异常响应保护块。与finally不同的是:正常情况下except 后面的语句并不被执行,而当异常发生时程序自动跳到except处,进入异常响应处理模块。当异常被响应后异常类自动清除。
下面是异常响应方式的一般代码:
try {程序正常功能} except on esomething do {响应特定异常} else {提供默认响应} end;保留字on„do用于判断异常类型。必须注意的是:except后面的语句必须包含在某一个on„do模块中,而不能单独存在。这是又一个与finally不同的地方。
3.4.4 提供默认响应
在异常响应模块中,一般我们只对希望响应的特定异常进行处理。如果一个异常发生而响应模块并没有包含对它的处理代码,则退出当前响应模块,异常类仍被保留。
为了保证任何异常发生后都能在当前响应模块中被清除,可以定义默认响应:
try {程序正常功能} except on esomething do {响应特定异常} else {提供默认响应} end;由于else可以响应任何异常,包括我们一无所知的异常,因此在默认响应中最好只包括诸如显示一个消息框之类的处理,而不要改变程序的运行状态或数据。
第3章 “速算24”扑克游戏--单元、异常、逻辑
3.5 数学逻辑单元(1)
此游戏程序最关键的地方是如何将用户输入的字符串表达式解析成数学表达式。为了使程序结构清晰明了,我们将此解析代码和程序的主代码分开,单独编写成一个单元。
3.5.1 算法设计
游戏的难点是如何将一字符串形式的表达式解析成计算机能计算的算术表达式。例如对于字符串“3^(4*(9+4))”,如何让计算机解析、计算。
我们的想法是按照数学四则运算规则,先逐层进入最里层的括号,然后在括号内部计算乘方,接着进行乘(除)法运算,最后按顺序进行加(减)运算,当本层括号内部计算完成后,返回结果,去掉括号内部数据,退出到下一级括号(如果有)内进行计算。
这里面涉及的技术细节主要有下面几点:
(1)层层剥离括号,然后从最里层的括号开始计算。(2)对于每一个运算符号,找到符号两侧的数字,形成一个计算式。
(3)每一个子计算式完成后,运算结果返回到原始数列中,作为子串的一部分,继续进行上述计算。
3.5.2 字符串的相关函数
在游戏中,用户输入的都是字符数据,我们需要从这些字符中分析得到数字和运算符号,因此要用到与字符串操作有关的函数。
function pos(sub , all:string):integer;这个函数含有两个参数:sub表示要查找的字符,all表示原字符串。函数在字符串all中寻找指定的字符sub的位置,如果字符串中不存在sub字符,则函数结果为0。
function lastdelimiter(sub,all :string):integer 这个函数含有两个参数:sub表示要查找的字符,all表示原字符串。函数返回在字符串all中所有查找到的指定字符sub的最后一个的位置,如果字符串中不存在sub字符,则函数结果为0。
function copy(allstring:string;first,length:integer):string 这个函数的3个参数的含义分别是:allstring代表原来的字符串,first表示拷贝开始的位置,length表示要拷贝的子串长度。函数返回拷贝成功的子串。
procedure delete(str:string;ppos,length:integer)这个过程用于删除字符串中的一段字符。参数str代表将要操作的字符串,ppos代表开始删除的位置,length表示将要删除多少个字符。function length(s): integer;length函数返回字符串s的长度。
function trim(const s: string): string;overload;function trim(const s: widestring): widestring;overload;trim函数返回字符串s去掉前面和后面的空格后的字符串。
下面的例子给出一个综合利用字符串处理函数编写的一个处理特定字符串的函数,它的功能是:输入一个字符串后,可以返回字符串中两个单括号之间的子字符串,并去掉前面和后面带着的空格:
function getmystr(const s: string): string;begin result:=trim(copy(s,pos('<',s)+1,pos('>',s)-pos('<',s)-1));end;比如我们在程序中写到getmystr(‘this is a test < result to output > end of test’);,会得到字符串“result to output”。
3.5.3 算法的代码编写
基于上述的考虑和知识基础,我们在声明部分定义下列几个主要函数:
(1)anylastpos函数定位最后一个算术运算符的位置。
function anylastpos(str:string):integer;(2)anyfirstpos函数定位最先一个算术运算符的位置。
function anyfirstpos(str:string):integer;(3)anyfirstf函数判断最先出现的符号是+号、-号、*号还是/号。
function anyfirstf(str:string):char;(4)subcompute函数用于计算不带()号的加、减、乘、除运算。
function subcompute(str:string):integer;(5)totalcompute函数用于计算表达式的结果。
function totalcompute(str:string):integer;1.寻找最后一个算术运算符
定义4个整数变量subpos,plupos,mulpos,spanpos,在给定的字符串中寻找+,-,*,/的最后位置,将这些位置存储在上述的4个变量中,然后比较4个符号出现的位置,得到数值最大的运算符;在返回的结果中,返回这个运算符的位置。
程序代码如下所示:
function anylastpos(str:string):integer;var subpos:integer;plupos:integer;mulpos:integer;spanpos:integer;pos:integer;begin //定位字符串中最后一个运算符的位置
subpos:=lastdelimiter('-',str);plupos:=lastdelimiter('+',str);mulpos:=lastdelimiter('*',str);spanpos:=lastdelimiter('/',str);pos:=subpos;if(pos
分别在给定的字符串中寻找+,-,*,/第一次出现的位置,然后比较4个符号出现的位置,得到数值最小的运算符。在返回的结果中,传递的是这个运算符的位置。
程序代码如下所示:
function anyfirstpos(str:string):integer;var subpos:integer;plupos:integer;mulpos:integer;spanpos:integer;forpos:integer;firstpos:integer;begin //定位字符串中最先一个运算符的位置
subpos:=pos('-',str);plupos:=pos('+',str);mulpos:=pos('*',str);spanpos:=pos('/',str);forpos:=pos('^',str);firstpos:=200;if(subpos=0)then //如果没有-号
subpos:=200;//将subpos设置成一个不可能的值
if(plupos=0)then //如果没有+号
plupos:=200;//将plupos设置成一个不可能的值
if(mulpos=0)then //如果没有*号
mulpos:=200;//将mulpos设置成一个不可能的值
if(spanpos=0)then //如果没有/号
spanpos:=200;//将spanpos设置成一个不可能的值
if(forpos=0)then //如果没有^号
forpos:=200;//将forpos设置成一个不可能的值
if(firstpos>subpos)then firstpos:=subpos;if(firstpos>plupos)then firstpos:=plupos;if(firstpos>mulpos)then firstpos:=mulpos;if(firstpos>spanpos)then firstpos:=spanpos;if(firstpos>forpos)then firstpos:=forpos;
anyfirstpos:=firstpos;//结束函数,返回位置
end;第3章 “速算24”扑克游戏--单元、异常、逻辑
3.5 数学逻辑单元(2)
3.得到最先出现的运算符类型
这个函数的返回结果是char类型,代表这是一个字符变量。实际上,它返回的是+、-、*、/ 这4个符号中最早出现的一个。
程序分别寻找4个符号最早出现的位置,然后判断最先出现的是哪一种符号,再根据符号类型返回代表运算符的字符。
在具体的实现过程中,因为我们要得到最先出现的运算符,所以判断的是每次寻找后各个运算符的位置的最小值。如果不存在这个运算符,则将代表这个运算符位置的相应变量设置为200。对于本程序来说,这是一个搜索过程中不可能达到的值,这样就排除了这个位置的继续比较的可能。
程序代码如下所示:
function anyfirstf(str:string):char;var subpos:integer;plupos:integer;mulpos:integer;spanpos:integer;operator:char;temppos:integer;begin subpos:=pos('-',str);plupos:=pos('+',str);mulpos:=pos('*',str);spanpos:=pos('/',str);
if(subpos=0)then //如果没有-号
subpos:=200;//将subpos设置成一个不可能的值
if(plupos=0)then //如果没有+号
plupos:=200;//将plupos设置成一个不可能的值
if(mulpos=0)then //如果没有*号
mulpos:=200;//将mulpos设置成一个不可能的值
if(spanpos=0)then //如果没有/号
spanpos:=200;//将spanpos设置成一个不可能的值
operator:='-';temppos:=subpos;if(temppos>plupos)then begin temppos:=plupos;operator:='+';end;if(temppos>mulpos)then begin temppos:=mulpos;operator:='*';end;if(temppos>spanpos)then begin temppos:=spanpos;operator:='/';end;
anyfirstf:=operator;//结束函数,返回位置 end;4.计算不带括号的运算表达式
做完上述工作后,我们可以开始进行一些实际的运算了。
包括加、减、乘、除、乘方运算的表达式的程序算法如下所示:
(1)寻找乘方符号“^”,如果存在,则计算一次乘方,去掉计算过的部分,接着循环查找和计算子串的乘方。
(2)寻找乘号“*”或者除号“/”,如果存在,则计算一次乘(除)法,去掉计算过的部分,接着循环计算子串的乘除法。
(3)寻找加号“+”或者减号“-”,如果存在,则计算一次加(减)法,去掉计算过的部分,接着循环计算子串的加减法。
上述算法是严格按照顺序进行的,它体现了数学运算中的优先关系,经过上述的计算,子字符串被分解,计算完毕。
无论是乘方、乘除法还是加减法,内部实现的逻辑是基本一致的。下面,我们设定有一个运算表达式:3+2^5/4。
程序代码如下所示:
function subcompute(str:string):integer;var middle:string;mul2:string;right:string;first:integer;tempstr:string;temp:integer;left:string;mul1:string;mulpos:integer;spanpos:integer;fuhao:char;begin middle:='';mul2:='';right:='';
//定位第一个^号位置,计算乘方
first:=pos('^',str);while(first<>0)do //循环计算乘方
begin tempstr:=copy(str,1,first-1);temp:=anylastpos(tempstr);left:=copy(str,1,temp);mul1:=copy(str,temp+1,first-temp-1);tempstr:=copy(str,first+1,length(str)-first);temp:=anyfirstpos(tempstr);if(temp=200)then begin mul2:=tempstr;right:='';end else begin mul2 :=copy(tempstr,1,temp-1);right:=copy(tempstr,temp,length(tempstr)-temp+1);end;middle:=floattostr(intpower(strtoint(mul1),strtoint(mul2)));str:=left+middle+right;first:=pos('^',str);end;
//定位第一个*号或/号的位置
mulpos:=pos('*',str);spanpos:=pos('/',str);first:=mulpos;if(mulpos>spanpos)then first:=spanpos;if((spanpos=0)and(mulpos<>0))then begin first:=mulpos;spanpos:=2000;// 将除号所在位置设置成一个大于mulpos但又不可能的值
end;if((spanpos<>0)and(mulpos=0))then begin first:=spanpos;mulpos:=2000;// 将乘号所在位置设置成一个大于spanpos但不可能的值
end;while(first<>0)do //循环计算乘、除
begin tempstr:=copy(str,1,first-1);temp:=anylastpos(tempstr);left:=copy(str,1,temp);mul1:=copy(str,temp+1,first-temp-1);tempstr:=copy(str,first+1,length(str)-first);temp:=anyfirstpos(tempstr);if(temp=200)then begin mul2:=tempstr;right:='';end else begin mul2 :=copy(tempstr,1,temp-1);right:=copy(tempstr,temp,length(tempstr)-temp+1);end;if(mulpos>spanpos)then middle:=inttostr(strtoint(mul1)span strtoint(mul2))else middle:=inttostr(strtoint(mul1)*strtoint(mul2));str:=left+middle+right;
mulpos:=pos('*',str);spanpos:=pos('/',str);first:=mulpos;if(mulpos>spanpos)then first:=spanpos;
if((spanpos=0)and(mulpos<>0))then begin first:=mulpos;spanpos:=2000;// 将除号所在位置设置成一个大于mulpos但又不可能的值
end;if((spanpos<>0)and(mulpos=0))then begin first:=spanpos;mulpos:=2000;// 将乘号所在位置设置成一个大于spanpos但不可能的值
end;end;//定位+、-号首先出现的位置
first:=anyfirstpos(str);if(first=200)then //如果没有+、-号,则可以直接返回结果
begin subcompute:=strtoint(str);exit;end;fuhao:=anyfirstf(str);//确定首先出现的符号是+号还是-号
while(first<>0)do begin //如果找到+号或-号
tempstr:=copy(str,1,first-1);temp:=anylastpos(tempstr);left:=copy(str,1,temp);mul1:=copy(str,temp+1,first-temp-1);tempstr:=copy(str,first+1,length(str)-first);temp:=anyfirstpos(tempstr);if(temp=200)then begin mul2:=tempstr;right:='';end else begin mul2 :=copy(tempstr,1,temp-1);right :=copy(tempstr,temp,length(tempstr)-temp+1);end;if(fuhao='+')then middle:=inttostr(strtoint(mul1)+strtoint(mul2))else middle:=inttostr(strtoint(mul1)-strtoint(mul2));str:=left+middle+right;first:=anyfirstpos(str);if(first=200)then break;fuhao:=anyfirstf(str);end;
subcompute:=strtoint(middle);end;程序执行过程如下所示:
(1)定位字符串中第一个乘方符号“^”的位置first。这个式子中的first为4。
(2)如果存在乘方符号,即first不等于0,则继续进行计算,否则退出循环。
(3)进入循环体内部,得到“^”前面的子串tempstr(“3+2”),寻找tempstr中的最后一个运算符temp(这里是“+”),则temp和first之间的字符就是乘方符号的第一个参数(“2”)。
(4)同样的逻辑,得到“^”后面的子串tempstr(“5/4”),寻找tempstr中的第一个运算符位置temp(“/”),则temp和first之间的字符就是乘方符号的第二个参数(“5”)。
(5)去掉乘方符号和两个参数,得到左侧子串left(“3+”)和右侧子串right(“/4”)。
(6)利用这两个参数和乘方符号,计算乘方,将结果返回,并插入在left和right之间,得到一次计算后的新字符串(“3+32/4”)。
(7)判断新串内部是否包含“^”,如果包含,则返回到步骤(3),不包含则进入下一种运算。
第3章 “速算24”扑克游戏--单元、异常、逻辑
3.5 数学逻辑单元(3)5.计算整个表达式的值
totalcompute函数利用循环,找到最内层的一对括号,然后调用subcompute函数处理这一对括号中的表达式。subcompute函数处理的表达式中已经没有括号了,因此subcompute只需处理乘方、加、减、乘、除,返回结果,形成新的字符串。
当整个字符串缩减至空值时,整个表达式计算完成。
程序代码如下所示:
function totalcompute(str:string):integer;var first:integer;last:integer;substr:string;leftstr:string;middle:string;right:string;temp:integer;begin first:=lastdelimiter('(',str);//定位最后一个(号位置 while(first<>0)do begin substr:=copy(str,first+1,length(str)-first);last:= pos(')',str);//last:=last+first;//定位最后一个(号以后的最开始的)号位置
leftstr:=copy(str,1,first-1);//(号左边的字符串
middle:=copy(str,first+1,last-first-1);//()号中间的字符串
right:=copy(str,last+1,length(str)-last);//)号右边的字符串
temp:=subcompute(middle);//进入下面的计算
middle:=inttostr(temp);
str:=leftstr+middle+right;first:=lastdelimiter('(',str);end;
result:=subcompute(str);end;end.在程序中,“计算”按钮的onclick事件处理程序中调用totalcompute函数。函数中使用了一些数学函数和定位字符串的函数,这些函数delphi已经在相应的系统单元中进行了定义,我们需要把这些系统单元包括到文件里面:
uses sysutils,math;将前面调用totalcompute的注释去掉,把代码改回:
procedure 2click(sender: tobject);var result:integer;answer:string;begin result:=totalcompute();if(result=24)then ebox('您真行,我服了您!','对了',mb_ok)else begin