VC++60C++实验报告值得学习.docx
《VC++60C++实验报告值得学习.docx》由会员分享,可在线阅读,更多相关《VC++60C++实验报告值得学习.docx(39页珍藏版)》请在冰豆网上搜索。
VC++60C++实验报告值得学习
C++实验报告
实验一熟悉VisualC++6.0环境
一、实验目的
1.熟悉实验设备、环境;熟练掌握VisualC++6.0环境;
2.掌握调试程序的操作步骤;初步了解程序的结构。
二、实验原理
使用C++语言编写源程序后,源程序在计算机上的实现与其它高级语言源程序实现的原理是一样的一般都要经过下述三个步骤:
(1)编辑:
即将编写好的C++源程序输入到计算机当中,生成磁盘文件的过程。
(2)编译:
C++语言的编译器可将程序的源代码转换成为机器代码的形式即目标代码,然后再对目标代码进行连接,生成可执行文件,等待下一步的执行过程。
(3)运行:
即在编译系统中运行源程序经过编译和连接后生成的可执行文件。
三、实验内容
(一)启动VisualC++6.0,输入并调试执行C源程序
1.启动VisualC++6.0开发环境:
打开 “MicrosoftVisualStudio 6.0”→“MicrosoftVisualC++ 6.0”,如图1-1所示。
图1-1
2.编辑源程序文件:
单击菜单栏上的“文件(File)”|“新建(New) Ctrl+N”按扭,如图1-2所示;在弹出的“新建”对话框中选中“文件”选项卡,选择“C++SourceFile”,并在文件(File)编辑框内输入文件名(本例中为test_1.c),在位置(Location)编辑框中指定文件存放位置,如图1-3所示。
注意观察窗口的变化
图1-2
图1-3
单击“确定(ok)”按钮。
在工作窗口中输入下面的简单程序,注意程序的格式(程序的注释部份也可以输入),如图1-4所示。
图1-4
#include /* 包含头文件,告诉编译程序(VisualC++6.0),本程序将使用到输入输出函数 */
voidmain()/* 主函数,程序的入口。
每一个C程序必须有一个主函数,而且只有这一个 */
{
int a,b,c; /* 定义三个整型变量 */
a=2; b=3; /* 给两个变量赋值 */
c=a+b; /* 计算 */
printf("%d,%d\n",c,a+b);/* 用整数方式输出运算结果,并换行。
比较c的值与a+b的值相同吗?
*/
}
提示:
当输入完或修改了源程序后,注意保存源程序文件。
3.编译源程序:
打开“组建(Build)|编译(Compile)”菜单的“编译Test_1.c Ctrl+F7”命令(或者按快捷键Ctrl+F7),将程序编译成目标文件,其扩展名为.OBJ,如图1-5所示。
注意观察窗口的变化。
图1-5
若程序编译后有语法错,需要回到工作窗口,改正错误后重新编译程序。
注意观察工作窗口下方信息窗口中的提示信息,结合程序中错误的语句理解提示信息的含义。
特别提醒:
改正程序中的错误时,每次都要从第一个错误开始。
注意:
若此时语言系统开始编译后不能停下来(编译微型工具栏变灰),则可先存盘(或复制),结束VisualC++6.0,然后重新启动VisualC++6.0,打开源程序再编译。
4.连接目标程序:
打开“组建(Build)”菜单的“构件Test_1.exe F7”命令(或者按快捷键F7),将程序连接成可执行文件,其扩展名为.exe,如图1-6所示。
同样,若程序连接后有错,需要回到工作窗口,改正错误后重新编译、连接程序。
图1-6
5.执行程序:
打开“组建(Build)|执行(Execute)”菜单的“!
执行Test_1.exe Ctrl+F5”命令(或者按快捷键Ctrl+F5),执行Test_1.exe文件,执行结果如图1-7所示:
图1-7
此时,按下任意键就可以关闭输出窗口,回到VisualC++6.0集成环境。
至此,已经完成了一个C源程序上机执行的过程。
但是,程序中可能还存在着因编程者对问题解决方案的错误理解导致程序运行结果不符合问题要求的情况,称逻辑错误。
这类错误最难排除。
要排除逻辑错误,首先要找到错误发生的地方。
可以借助集成环境中的调试(Debug)工具(或在关键点上插入输出语句)来观察相关变量的值是否与编程者设想的一致。
当发现某处不一致时,就定位出逻辑错误所在的源代码。
完成上机任务后,可以退出VisualC++6.0环境。
6.退出VisualC++6.0环境:
打开“文件”菜单选择“退出(Exit)”命令即可。
若上机过程中需要调试另一个C源程序,可以关闭当前源程序而不退出VisualC++6.0环境。
关闭工作区:
打开“文件”菜单选择“关闭工作区(CloseWorkspace)”命令即可关闭当前正在工作的文件,如图1-8所示。
图1-8
再次上机时,可以调出已存储在磁盘上的C源程序,查看、修改或编译执行。
7.打开一个已经存在的程序:
打开“文件”菜单选择“打开(Open)”命令选项,即可弹出相应的对话框,选择待打开的文件,双击该文件名或者单击“打开”按钮,可重复上面的3~5步完成编辑、编译、链接、运行等操作。
四、实验思考讨论与心得体会
(1)思考讨论
1.完成本实验需要哪些语句结构。
答:
此实验相对比较简单,只是使用了表达式语句结构。
2.你是怎么把命令发给计算机,并要求其执行的?
答:
C语言只是一种程序,能够把自然语言改成机器语言的翻译程序,我们可以利用C程序将我们的C++高级语言编译形成计算机本身所能识别的机器语言,进而实现计算机对我们所发送给它的指令的执行。
(2)心得体会
C++是一种面向对象的程序设计语言,使用它可以实现面向对象的设计。
通过此次实验,我熟练地掌握了它的基本用法,这对以后进一步的编程、调试奠定了基础。
另一方面,就是感觉整个流程走下来,还是相对蛮简单以及人性化。
实验二构造函数和析构函数
一、实验目的
1、清楚构造函数的作用,掌握构造函数的声明、定义方法;
2、掌握重载构造函数、带参数构造函数的定义、应用;
3、熟悉用参数列表初始化数据成员的书写方式;
4、清楚析构函数的作用,掌握析构函数的声明、定义方法;
5、熟悉构造函数、析构函数的调用顺序。
二、实验原理
构造函数和析构函数是在类体中说明的两种特殊的成员函数。
构造函数的功能是在创建对象时,用给定的值对对象进行初始化。
析构函数的功能是用来释放一个对象,与构造函数的功能刚好相反。
三、实验程序
#include
usingnamespacestd;
classTest
{
private:
intx;
public:
Test()
{
cout<<"对象地址:
"<x=0;
}
~Test()
{
cout<<"对象地址:
"<}
voidprint()
{
cout<<"数据成员:
x="<}
};
voidmain()
{
Testobj1,obj2;//创建对象时,自动调用构造函数
obj1.print();
obj2.print();
}
四、实验结果及程序分析
(1)实验结果:
将该实验程序放于visualc++6.0上运行,得到如下结果,见截图所示:
(2)程序分析
该程序中,定义了两个对象obj1和obj2,并对它们进行了初始化,初始化是由构造函数实现的,而构造函数又是自动调用的。
从输出结果中可以看出,调用了两次构造函数。
析构函数在该程序中也被调用了两次,同时也是自动调用的。
五、实验思考讨论与心得体会
1.思考讨论
(1)析构函数有什么作用?
在书写时,与构造函数有什么相同点和不同点?
答:
作用:
析构函数是用来释放对象的。
在书写时,只要在构造函数的前面加上“~”即可。
(2)程序中的this代表什么?
this能否写在main()函数中?
答:
this代表一个指向正在对某个成员函数操作的对象的指针。
不能写在main()函数中,只能在一个类的成员函数中调用,表示当前对象的地址。
(3)构造函数、析构函数的调用顺序怎样?
答:
先调用构造函数再调用析构函数。
2.心得体会
我们在对类的对象进行初始化时要调用构造函数,另一方面,释放时要调用析构函数。
通过此次实验,我掌握了二者的基本用法和其各自独具的特点,对以后类的学习及使用大有裨益。
实验三常类型与类型转换
一、实验目的
(1)熟悉常类型的定义、内容以及类型转换中自动隐式转换和强制转换两种类型的转换规则。
(2)掌握定义或说明常类型量时进行初始化的方法
(2)学会如何定义类型转换函数及其使用。
二、实验原理
常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。
常类型包括一般常量和对象常量、常指针和常引用、常成员函数和常数据成员。
它们都有自己的定义和使用规则,要求熟悉掌握。
类型转换可将一种类型的值映射为另一种类型的值,分为自动隐式转换和强制转换,其中,强制转换又包括构造函数的类型转换与用户自己定义的类型转换函数,它们在实际应用中都发挥着一定的作用。
三、实验程序
(1)常类型
(2)类型转换
四、实验结果及程序分析
(1)常类型
结果如下截图所示:
程序分析:
该程序中,说明了如下三个常类型数据成员:
constint&r
constinta
staticconstintb
其中,r是常int型引用,a是常int型变量,b是静态常int量,而且静态数据成员b是在类体外进行初始化的。
这一点应注意。
(2)类型转换
结果如下截图所示:
程序分析:
表达式语句d+=r之所以能够顺利进行,是因为该程序使用了类型转换函数operatordouble()。
为使上述加法能够顺利进行,编译系统先检查Rational的说明,看是否存在一个类型转换函数能够将Rational类型的操作数转换为double类型的操作数。
由于Rational类中说明了类型转换函数operatordouble(),所以上述类型转换成功。
五、实验思考讨论与心得体会
1.思考讨论
(1)判断对错:
intconstm=15;m=18;
答:
错误。
因为前面定义了m是一个常量,并且对它初始化了,因此不能改变m的值了。
(2)自动隐式转换规则有哪些?
我们在定义类型转换函数时应注意什么?
答:
自动隐式转换规则:
算术运算时,低类型到高类型;赋值表达式中,右边=左边;函数调用时,实参转换为形参的类型;函数有返回值时,返回值类型转换为函数类型。
定义类型转换函数时应注意:
它是非静态的成员函数,不可以有返回值,不带任何参数,不能定义为友元函数。
2.心得体会
通过此次实验,我收获颇丰。
我不仅仅解决了以前关于类型转换这样那样的问题,还全面的了解到了类型转换函数这个新鲜事物。
当然,常类型以及自动隐式转换中也有之前不为我所知的。
我想,此次实验之后,以后再碰到程序中关于类型不符的问题都可以迎刃而解了。
实验四派生类的构造函数与虚基类
一、实验目的
1. 掌握单继承和多重继承下派生类的定义方法,理解基类成员在不同的继承方式下不同的访问属性;
2. 正确定义派生类的构造函数与析构函数,理解定义一些个派生类对象时各个构造函数、析构函数被调用的顺序;
3. 正确定义虚基类,消除在多层次多重继承方式下顶层基类中成员访问的二义性问题,关注此时各构造函数、析构函数的调用顺序。
4.通过基类与公有派生类的定义,及基类对象、指针、引用与派生类的对象、地址间相互赋值的方法,正确理解赋值兼容的4种情况,通过程序理解其不可逆性。
二、实验原理
1.派生类的数据成员由所有基类的数据成员与派生类新增的数据成员共同组成,如果派生类新增成员中包括其他类的对象(子对象),派生类的数据成员中实际上还间接包括了这些对象的数据成员。
因此,构造派生类的对象时,必须对基类数据成员、新增数据成员和成员对象的数据成员进行初始化。
派生类的构造函数必须要以合适的初值作为参数,隐含调用基类和新增对象成员的构造函数,来初始化它们各自的数据成员,然后再加入新的语句对新增普通数据成员进行初始化。
派生类构造函数的一般格式如下:
<派生类名>:
:
<派生类名>(<参数表>):
<基类名1>(<参数表1>),
……,
<基类名n>(<参数表n>),
<子对象名1>(<参数表n+1>),
……,
<子对象名m>(<参数表n+m>)
{
<派生类构造函数体> //派生类新增成员的初始化
}
2.在多继承中,如果类B1和类B2分别继承了类A,,类C又继承了类B1和B2。
那么,在该结构中,类C的对象将包含两个类A的子对象。
由于类A是派生类C两条继续路径上的一个公共基类,那么这个公共基类将在派生类的对象中产生多个基类子对象。
假如要想使这个公共基类在派生类中只产生一个基类子对象,则必须将这个基类设定为虚基类。
因此为了彻底避免在这种结构中的二义性,在创建派生类对象时对公共基类的数据成员只进行一次初始化,便引入了虚基类。
虚基类说明格式如下:
virtual<继续方式><基类名>
其中,virtual是虚类的关键字。
虚基类的说明是用在定义派生类时,写在派生类名的后面。
例如:
classA
{
public:
voidf();
PRotected:
inta;
};
classB:
virtualpublicA
{
protected:
intb;
};
三、实验程序
(1)派生类的构造函数
#include
classA
{
public:
A()
{a=0;}
A(inti)
{a=i;}
voidprint()
{cout<private:
inta;
};
classB:
publicA
{
public:
B()
{b1=b2=0;}
B(inti){b1=i;b2=0;}
B(inti,intj,intk):
A(i),b1(j),b2(k)
{}
voidprint()
{A:
:
print();cout<private:
intb1,b2;};
voidmain()
{
Bd1;
Bd2(5);
Bd3(4,5,6);
d1.print();
d2.print();
d3.print();}
(2)虚基类
#include
classA
{
public:
A(constchar*s){cout<
~A(){}};
classB:
virtualpublicA
{public:
B(constchar*s1,constchar*s2):
A(s1)
{cout<classC:
virtualpublicA
{public:
C(constchar*s1,constchar*s2):
A(s1)
{cout<classD:
publicB,publicC
{public:
D(constchar*s1,constchar*s2,constchar*s3,constchar*s4)
:
B(s1,s2),C(s1,s3),A(s1)
{cout<voidmain()
{D*ptr=newD("classA","classB","classC","classD");
deleteptr;}
四、实验结果及程序分析
(1)派生类的构造函数:
程序分析:
该程序中,派生类B中定义了三个构造函数,前两个构造函数没有显示地调用基类构造函数,其实隐式地调用了基类A中的默认构造函数。
而第三个构造函数则显示地调用了A中的第二个构造函数。
(2)虚基类
程序分析:
在派生类B和C中使用了虚基类,使得建立的D类对象只有一个虚基类子对象。
在派生类B,C,D的构造函数的成员初始化列表中都包含了对虚基类A的构造函数。
而在建立类D对象时,只有类D的构造函数的成员初始化列表中列出的虚基类构造函数被调用,并且仅调用一次,而类D基类的构造函数的成员初始化列表中列出的虚基类构造函数不被执行。
这一点将从该程序的输出结果可以看出。
五、实验思考讨论与心得体会
1.思考讨论
(1)从此次实验中,可以得到关于派生类构造函数怎样的调用顺序?
答:
基类的构造函数;
子对象的构造函数(如果有的话);
派生类构造函数。
(2)对于虚基类的实验中,如果没有使用虚基类,结果将如何?
对比二者,可以得出什么样的结论?
答:
无虚基类:
classA
classB
classA
classC
classA
classD
由此可以得到结论:
含有虚基类的派生类的构造函数只被调用一次,其引入的目的就是为了解决多继承的二义性问题。
2.心得体会
派生类的构造函数和虚基类是在类使用过程中举足轻重的方面,我们如果掌握了其用法,必然可以为我们减少不必要的麻烦以及减轻编写程序的繁重负担。
特别是对于虚基类,我们要加以善用。
实验五运算符重载
一、实验目的
1.掌握用成员函数重载运算符的方法
2.掌握用友元函数重载运算符的方法
3.理解并掌握利用虚函数实现动态多态性和编写通用程序的方法
二、实验原理
C++中预定义的运算符的操作对象只能是基本数据类型。
但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作。
这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作。
运算符重载是通过创建运算符函数实现的。
运算符函数定义的一般格式如下:
<返回类型说明符>operator<运算符符号>(<参数表>)
{
<函数体>
}
运算符函数重载一般有两种形式:
重载为类的成员函数和重载为类的非成员函数。
非成员函数通常是友元。
这两种形式都可访问类中的私有成员。
1)
运算符重载为类的成员函数的一般格式为:
<函数类型>operator<运算符>(<参数表>)
{
<函数体>
}
2)
运算符重载为类的友元函数的一般格式为:
friend<函数类型>operator<运算符>(<参数表>)
{
<函数体>
}
三、实验程序
1.运算符重载为成员函数
#include
#include
usingnamespacestd;
classComplex
{
public:
Complex(){real=0;imag=0;}
Complex(doubler,doublei){real=r;imag=i;}
Complexoperator+(Complex&c2);
Complexoperator-(Complex&c2);
Complexoperator*(Complex&c2);
Complexoperator/(Complex&c2);
voiddisplay();
private:
doublereal;
doubleimag;
};
ComplexComplex:
:
operator+(Complex&c2)
{
Complexc;
c.real=real+c2.real;
c.imag=imag+c2.imag;
returnc;
}
ComplexComplex:
:
operator-(Complex&c2)
{
Complexc;
c.real=real-c2.real;
c.imag=imag-c2.imag;
returnc;
}
ComplexComplex:
:
operator*(Complex&c2)
{
Complexc;
c.real=real*c2.real-imag*c2.imag;
c.imag=imag*c2.real+real*c2.imag;
returnc;
}
ComplexComplex:
:
operator/(Complex&c2)
{
Complexc;
c.real=(real*c2.real+imag*c2.imag)/(c2.real*c2.real+c2.imag*c2.imag);
c.imag=(imag*c2.real-real*c2.imag)/(c2.real*c2.real+c2.imag*c2.imag);
returnc;
}
voidComplex:
:
display()
{cout<<"("<Intmain()
{
Complexc1(1,2),c2(-2,4),c3;
while
(1)
{
inta;
cout<<"请输入号码:
";
cin>>a;
switch(a)
{
case1:
{
c3=c1+c2;
cout<<"c1+c2=";
c3.display();
}break;
case2:
{
c3=c1-c2;
cout<<"c1-c2=";
c3.display();
}break;
case3:
{
c3=c1*c2;
cout<<"c1*c2=";
c3.display();
}break;
case4:
{
c3=c1/c2;
cout<<"c1/c2=";
c3.display();
}break;
case0:
{
a=0;
cout<<"运算符重载为成员函数"<returna;
}
default:
break;
}
}
return0;
}
2.运算符重载为友元函数
#include
classcomplex//复数类声明
{
public:
complex(doubler=0.0,doublei=0.0)
{real=r;imag=i;}//构造函数
friendintoperator>(complexc1,complexc2);
//运算