Presentation is loading. Please wait.

Presentation is loading. Please wait.

第1章 C++面向对象程序设计要点 1.1 函数和函数参数 1.2 输入输出   1.3 类 1.4 抽象类型和模板.

Similar presentations


Presentation on theme: "第1章 C++面向对象程序设计要点 1.1 函数和函数参数 1.2 输入输出   1.3 类 1.4 抽象类型和模板."— Presentation transcript:

1 第1章 C++面向对象程序设计要点 1.1 函数和函数参数 1.2 输入输出   1.3 类 1.4 抽象类型和模板

2 1.1 函数和函数参数 1.1.1函数类型 C++中有两种类型函数:常规函数和成员函数。常规函数用于完成一个特定的功能,成员函数用于类方法的定义。无论是常规函数还是成员函数,其定义都包括四个部分:函数名、形式参数表、返回类型和函数体。

3 函数的使用者通过函数名来调用函数,调用过程是把实际参数表传送给形式参数表作为函数的数据来源,然后执行函数体中的语句实现该函数的功能,最后得到的返回值由函数名带回函数的调用者。成员函数是用于类方法定义的,因此,只有该类的对象才能调用其成员函数。

4 1.1.2 函数名重载 C++允许函数名重载,即允许若干个相同的函数名能在相同作用域内使用。编译系统根据函数引用的参数类型和参数个数判明函数的使用。 例1―1函数名重载 #include<stdio.h> int max(int a,int b) { if(a>=b)return a; else return b; }

5 Float max(float a,float b,float c)
{ if(a>=b&&a>=c)return a; else if(a<b&&b>c)return b; else return c; } void main() printf("max(2,3)=%d\n",max(2,3)); printf("max(2.3,5.6,3.1)=%f\n",max(2.3,5.6,3.1));

6 函数参数 C++中函数(包括常规函数和成员函数)参数有四种方式:值参数、常值参数、引用参数和常值引用参数等方式。但常值参数方式因无必要,一般不用。要注意的是,不同方式的参数(不同方式的返回值也类同)在函数的传递调用中系统将视为不同的数据类型,如定义有函数f1(intx),

7 在函数f1()中又要调用函数f2(),如果函数f2()的定义是f2(constintx),系统将视int和constint为不同的数据类型,从而告知用户数据类型不匹配。int和int&的情况也一样。注意到这一点将可免去许多程序调试中的问题。 例1―2函数参数的值参数、引用参数和常值引用参数三种方式。 int Example1(int a,int b,int c) //值参数方式 { intx,y,z; x=a;y=b;z=c;  

8 a=2*a;b=2*b;c=2*c; //函数返回后对应实际参数的值不变
return(x+y+z)/3; } intExample2(int& a,int& b,int& c)//引用参数方式 { int x,y,z; x=a;y=b;z=c; a=2*a;b=2*b;c=2*c;//函数返回后对应实际参数的值改变

9 intExample3(const int& a,const int& b,cons tint&c) //常值引用参数方式
{ return(a+b+c)/3; //不允许改变常值引用参数a,b,c的值 }

10 引用参数的方法是在类型名后加符号&,常值引用参数的方法是在引用参数方法的基础上再在函数定义行的最前边加保留字const。
对于Example1的值参数方式,在运行时,对应的实际参数的值拷贝给形式参数,当函数终止时,形式参数的值不拷贝回实际参数。因此,值参数方式的函数调用不会改变对应形式参数的实际参数的值。  

11 1.1.4成员函数返回值 和函数参数的方式类同,成员函数返回值也有值方式、常值方式、引用方式和常值引用方式等四种方式。 当成员函数的返回值为值方式时,允许改变该对象的私有成员数据。注意,成员函数的返回值为无const标识符的任何数据类型(包括void类型)时即为值方式。

12 例1―3成员函数返回值的值方式和常值方式 #include<iostream.h> class Temperature { private: float high Temp //最高温度 float low Temp; //最低温度

13 public: Temperature(float h,float l){high Temp=h;lowTemp=l;}; void Update Temp(float temp);//成员函数返回值为值方式 float GetHigh Temp(void)const;//成员函数返回值为常值方式 float GetLowTemp(void)const;//成员函数返回值为常值方式 }; void Temperature::Update Temp(float temp) {  

14 if(temp>highTemp)highTemp=temp;//允许更改私有数据
if(temp<lowTemp)lowTemp=temp;//允许更改私有数据 } float Temperature::GetHigh Temp(void)const { return highTemp; //不允许更改私有数据 float Temperature::Get Low Temp(void)const return low Temp; //不允许更改私有数据

15 输入输出   C++提供了一个层次结构的I/O流类来支持输入输出。I/O流类的类层次结构关系如图1―1所示。流是一个非常形象的术语,每一个流类指定了一类文件和这类文件数据流动的方向。当我们定义一个具体的流类对象时,就确切地指定了这个文件的存储区和由系统设定的内存缓冲区,以及这个文件在其存储区和内存缓冲区之间的数据流动方向。

16 ios类是基类,它提供了格式、状态信息和错误检测。类ios中没有定义实际的输入和输出操作。实际操作是由派生类istream、ostream等提供的。两种基本的输入输出方式是:键盘屏幕(默认)输入输出和文件输入输出。  

17 图1―1 I/O流类的类层次

18 1.2.1 键盘屏幕输入输出 iostream流类中定义了键盘输入类、屏幕输出类和错误信息输出类。当iostream流类包含在应用程序中时,cin、cout和cerr分别为键盘输入类、屏幕输出类和错误信息输出类的系统默认对象。cin对象键盘输入的操作符为>>,cout和cerr对象屏幕输出的操作符为<<。

19 #include<iostream.h>
void main(void) { char c; int n; cout<<"输入n="; cin>>n; cout<<"n="<<n<<endl; }

20 1.2.2文件输入输出 调用打开文件函数时,打开文件函数的参数包括文件名和数据流动的方向,打开文件函数返回文件的开始地址。系统在存储文件时,在其末尾添加有文件结束标记,这样打开文件函数就具体地指定了一个文件在内存中的起始位置、结束位置和数据流动的方向。 fstream.h流类中定义了文件输入类和文件输出类。文件输入类的类名为ifstream,文件输出类的类名为ofstream。

21 #include<fstream.h>
#include<iostream.h> #include<stdlib.h> //包含exit()函数  Void main(void) { ifstream fin; //定义fin为ifstream 类的对象 ofstream fout; //定义fout为ofstream类的对象 fout.open("exam.dat",ios::out);//建立输出文件exam.dat

22 //输出字符串"wang"和数字50到输出文件exam.dat中(可打开该文件查看)
char name[]="wang",name2[10]; int age=50,age2; fout<<name<<endl; fout<<age<<endl; fin.open("exam.dat",ios::in|ios::nocreate);//打开输入文件exam .dat if (!fin) {

23 cerr<<"不能打开文件exam.dat"<<endl;
exit(1); } char c; //从输入文件exam.dat中输入字符串"wang"和数字50到name2和age2中 fin>>name2>>c>>age2; //屏幕输出查看 cout<<"name2="<<name2<<endl; cout<<"age2="<<age<<endl;

24 类   当开发的应用程序的数据类型不是int、float、char等系统支持的基本数据类型时,开发者就需定义自己的数据类型。C++中定义自己的数据类型的面向对象方法是使用类。 类是C++支持面向对象程序设计的基础。C++中定义类使用标识符class,类定义包括类中数据的定义和成员函数的定义。

25 例1―4设计一个人民币类,其方法包括设置、加(+)、加等于(+=),以及输出流操作<<,并用一个主函数验证。
#include<iostream.h> #include<stdlib.h> enum sign{minus,plus}; //正负数的符号标识 //类Money定义部分 class Money //类Money定义

26 { friend ostream&operator<<(ostream&out,constMoney&x); //友元 private: //私有部分 longamount;//以分为单位的钱数,amount为类Money 的数据部分 public: //共有部分 //构造函数 Money(signs=plu s,unsigned longd=0,unsigned longc=0); //析构函数  ~Money(){};

27 //共有成员函数 Bool Set(sign s,unsigned long d,unsigned long c); Money operator+(const Money& x)const; Money& operator+=(const Money & x) {amount+=x.amount;return*this;}//this是默认的当前对象指针 }; //类Money实现部分 Money::Money(signs,unsigned long d,unsigned long c) //构造函数。s为符号,d为元值,c为分值 { if(c>99)

28 { cerr<<"分必须小于等于99"<<endl; exit(1); } amount=d*100+c; if(s==minus)amount=-amount; bool Money::Set(sign s,unsigned long d,unsigned long c) //设置函数。s为符号,d为元值,c为分值 if(c>99)

29 exit(1); } amount=d*100+c; if(s==minus)amount=-amount; return true; Money Money::operator+(const Money&x)const//操作符+重载 { Money y; y.amount=amount+x.amount; return y;

30 ostream& operator<<(ostream& out, const Money& x)
//流类的操作符重载。 前边不再需标识符friend { Money temp = x;//注意临时对象temp绝对不 能定义为引用型 if(x.amount < 0 ) out << ′-′; temp = -temp; //temp为x的绝对值 } long d = temp / 100;//d为amount的元值

31 out << ′¥′ << d << ′.′;
int c = temp - d * 100; //c为amount的分值 if(c < 10) out << ′0′; //分值小于10时在十位上添0 out << c; return out; } //主函数部分 void main(void) { Money g, h(plus, 3, 30), hg;

32 g.Set(minus, 2, 25); hg = h + g; //h和g的值均不变 g += h; //g的值被改变 cout << "h + g = " << hg <<endl; cout << "g = " << g << endl; } 程序运行输出: h + g = ¥1.05 g = ¥1.05 下面我们对C++类基本要素的分析讨论将以例1―4为实例。

33 类的存取权限 类的数据和成员函数的存取权限分为三种: 私有(private)、公有(public)和保护(protected)。 在private域中, 声明的数据和成员函数构成类的私有部分, 私有部分中的数据和成员函数只能由该类对象的成员函数, 以及被声明为友元的函数或声明为友元的类的对象的成员函数访问。

34 在public域中,一般仅声明类的成员函数(有些情况下,也在此域中声明类的数据)。在public域中,声明的类的数据和成员函数构成类的公有部分;公有部分中的数据和成员函数既允许该类对象的成员函数访问,也允许程序中其他函数或其他类的对象的成员函数访问。因此,一个类的公有部分就构成了这个类的操作界面。外部函数和别的对象通过操作界面对类中的对象进行操作。这就和基本数据类型中int类数据均通过加(+)、减(-)、乘(*)、除(/)等操作界面来对该数据类型中所有数据进行操作一样。

35 构造函数和析构函数 构造函数是一种特殊的成员函数。构造函数是用来在内存中建立类的具体对象(即在内存中为该对象分配适当的空间)并对其进行初始化赋值的成员函数。构造函数的名字必须和类的名字相同,构造函数没有返回值。构造函数允许参数有默认值,当构造函数有默认值时,若定义该类的对象时没有给出初始化值则按默认值处理。例1―4中对象h有初始化值(plus,3,30),对象g和hg没有初始化值,因此对象g和hg的初始化值为默认值(plus,0,0)。一个类允许有多个构造函数。当一个类有多个构造函数时,系统将根据定义对象时的参数类型和参数个数,选择恰当的构造函数来建立对象和对该对象进行初始化。

36 析构函数是一种当对象被取消时才被自动调用的特殊函数。当一个对象被取消时,析构函数提供了自动释放由构造函数在内存中建立的该类对象的内存区。析构函数的名字是在类的名字前面加上前缀“~”,析构函数没有返回值。由于在一般情况下建立的对象当被取消时,系统都能自动识别出所占用的相应的内存区,所以一般情况下析构函数的函数体为空。函数体为空的析构函数可省略。但当对象被取消时系统不能自动识别出所占用的内存区的类,析构函数的函数体就不能为空,此时析构函数就要负责释放对象所占用的内存区。每个类只能有一个析构函数。

37 操作符重载 当所设计类的某个成员函数功能和人们熟悉的某个有固定操作符的函数功能类同时,可以用该操作符替换该成员函数名,这称作操作符重载。操作符重载时,操作符前使用标识符operator。对用户来说,操作符重载使类的成员函数的调用更加方便。如例1-4中的加(+)和加等于(+=)成员函数通过操作符重载方便了用户的调用。也就是说,一个类成员函数的操作符重载就是用一个有固定含义的操作符定义和实现该类的相应成员函数。

38 当调用时该操作符可以看成是该操作符左边对象的成员函数。如例1―4中的g+=hg可看作是加等于(+=)成员函数的对象,h可看作是加等于(+=)成员函数的参数。操作符重载不仅可以更方便地实现相应成员函数的功能,重载的操作符还具有相应的运算优先级和满足结合律,这是类的成员函数所没有的功能。

39 成员函数的操作符重载时必须满足: (1)操作符必须满足运算优先级和结合律,以及参数满足相应的操作符对操作数个数的要求,如二元操作加(+)必须有也只能有一个参数作为加数。 (2)C++中的所有操作符基本上都可被重载,但逗号操作(,)和条件表达式操作(?:)不允许重载。 (3)操作符重载时不允许参数有默认赋值。 (4)当重载的操作符左边的对象属类B,右边的对象属类A时,该重载的操作符应定义为类A的友元;否则类B的对象不能操作类A对象中的数据。

40 操作符重载不仅可以定义为成员函数,也可以定义为一般函数。当类的定义和实现已经编译完成,而在应用时具体定义的数据类型不支持类的定义和实现中使用的运算符时,定义为一般函数的操作符重载可不改变原先已编译完成的类的定义和实现。一般函数的二元操作符重载需有两个参数,这一点和成员函数的操作符重载不同。3.5.2节的小于(<)操作符的重载就是一个一般函数的例子,此时,原先已编译完成的类的定义和实现不用改动。

41 1.3.4友元 在例1―4中,Money类的输出成员函数和系统的输出流操作(<<)功能类同,因此我们也希望重载输出流操作符<<,但对于如下操作符重载定义ostream&operator<<(ostream&out,constMoney&x);由于该操作符左边的对象属类ostream,右边的对象属类Money,类ostream的对象不能存取类Money对象的私有数据amount,因而无法实现期望的输出流操作。

42 对于上述这种情况,我们可以在类Money的定义中定义类ostream的成员函数(或说操作符)<<是类Money的友元。在C++中一个类的友元可以存取这个类的私有数据。这样当我们在例1―4类Money的定义中有如下友元定义friendostream&operator<<(ostream&out,constMoney&x);则上述问题即可解决。友元定义使用标识符friend。一个类的友元不仅可以是其他类的成员函数,还可以是外部函数或其他类。当类A的友元是类B时,则类B中的所有成员函数均可存取类A的私有数据。

43 1.3.5 分辨符 在例1―4中,类的实现部分中类成员函数名前的符号::称为分辨符。分辨符指定了成员函数所属的类。当类的实现部分和类的定义部分不在一个文件中时,在实现部分的成员函数名前使用分辨符是必要的。此外,在一个有派生类存在的函数或程序中,若派生类和其基类有同名的成员函数时,在分辨符::前加上派生类名或基类名并放在该成员函数名前就可实现正确的访问。

44 1.3.6内联函数 C++类成员函数的实现部分中包括在定义部分的函数称为内联函数。编译系统在遇到内联函数时会将其实现部分的所有语句直接插入到调用程序中,因而减少了与函数调用和参数传递有关的系统开销,从而可以帮助C++克服在某些面向对象开发环境或开发工具中遇到的执行速度低的难题。由于内联函数增加函数调用的空间开销,因此通常只在实现部分语句较少或在要求系统的响应速度较高时才使用内联函数。

45 例1―3中类Temperature的构造函数和例1―4中类Money的加等于(+=)操作符重载都是内联成员函数的例子。外部函数的内联函数为在函数定义头部加标识符inline。当成员函数的实现部分较长时,定义内联函数的方法也是在成员函数头部加标识符inline来实现。

46 默认值 若定义类时,类的构造函数定义了默认值。定义对象有初始化值时,则该对象取其初始化值;定义对象没有初始化值时,则该对象取其默认值。在例1―4中,类Money的构造函数定义有默认值(plus,0,0)。类Money的构造函数定义语句为 Money(signs=plus,unsignedlongd=0,unsignedlongc=0)

47 1.3.8 多态性和虚函数 多态性是面向对象程序设计的一个重要概念。在面向对象语言中,用向一个对象发送消息的方式来完成一系列动作的实现。多态性是指当相同对象收到不同的消息或不同对象收到相同的消息时产生不同的实现动作。C++支持两种多态性:一种是编译时的多态性,另一种是运行时的多态性。编译时的多态性是通过重载函数来实现的。此时重载的函数有两种:一种是在一个类中说明的重载,此时靠参数的个数和参数的类型不同来区分;另一种是基类成员函数在派生类中的重载,

48 此时也可以靠参数的个数和参数的类型不同来区分,当参数的个数和参数的类型相同时还可以靠使用“类名::”加以区分。在C++中运行时的多态性是用虚函数来实现的。虚函数是在基类中被说明为virtual,并在派生类中重新定义的成员函数。虚函数允许函数调用与函数体之间的联系在运行时才建立,也就是在运行时才决定当前实际调用的是基类和派生类中哪一个类中的成员函数,因此,虚函数可实现成员函数的动态重载。

49 例1―5虚函数方法 #include<stdio.h> classParent { protected:
charversion; public: Parent(){version=′A′;} //构造函数 virtualvoidPrint() //虚函数 {printf("\nParentVersion%c",version);} voidTest() //测试继承方式用函数 {printf("\nPublicModeTest!");} };

50 classDerived1:publicParent
{ private: intinfo; public: Derived1(intnumber) //构造函数 {version=′1′;info=number;} voidPrint() {printf("\nDerived1info:%d,Version%c",info,version );} }; classDerived2:publicParent

51 { private: intinfo; public: Derived2(intnumber) //构造函数 {version=′2′;info=number;} voidPrint() {printf("\nDerived2info:%d,Version%c",info,version );} };

52 voidPrintInfo(Parent&infoHolder)
{ infoHolder.Print(); //根据不同的调用对象动态连接不同对象的成员函数 } voidmain(void) Parentbase; Derived1d1(10); Derived2d2(20); PrintInfo(base);

53 PrintInfo(d1); PrintInfo(d2); d1.Test(); //此语句只在public派生方式下允许 } 程序的输出是: ParentVersionA Derived1info:10,Version1 Derived2info:20,Version2 PublicModeTest!

54 在程序中,通过外部函数PrintInfo()实现类继承层次中对象的动态绑定。在程序中,若去掉类Parent中成员函数Print()前的virtual,则程序的输出是:
ParentVersionA ParentVersion1 ParentVersion2 PublicModeTest! 显然,若去掉类Parent中成员函数Print()前的virtual,则每次调用的都是类Parent的成员函数Print()。

55 纯虚函数和抽象类 有些基类只表达一些抽象的概念,并不和具体的事物相联系,所以在这些基类中定义某些成员函数是无意义的,但这些基类又必须为他的派生类提供一个公共接口界面,这样的基类可定义成纯虚函数。 纯虚函数是在基类中说明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加上“=0”。例如:

56 classParent { virtualvoidFunction()=0;//纯虚函数 }; 包含有纯虚函数的类称为抽象类。由于抽象类包含了没有函数定义的纯虚函数,所以不能定义抽象类的对象。当然,在抽象类中也可以定义非纯虚函数供派生类继承。

57 派生类继承方式 派生类继承基类时,基类的protected数据和成员函数将作为派生类的protected数据和成员函数。除此之外,派生类定义中的访问控制还定义了基类成员在派生类中的访问权限。访问控制主要有public和private两种继承方式。

58 在public继承方式中,基类的公有成员成为派生类的公有成员。在例1―5中,派生类Derived1和Derived2对父类Parent的继承是public继承方式。public继承方式的派生类对象能直接使用基类的所有public的数据和成员函数,因此,例1―5主函数中的d1.Test()调用的是Parent类的成员函数Test();private继承方式使基类的公有成员成为派生类的私有成员。

59 因此,在派生类中只允许其成员函数调用基类的公有成员函数,private继承方式的例子详见3. 3. 1节和3. 3
因此,在派生类中只允许其成员函数调用基类的公有成员函数,private继承方式的例子详见3.3.1节和3.3.2节顺序堆栈类对顺序表类的继承。无论是public继承方式还是private继承方式,基类的私有成员都成为派生类的不可访问成员。此外,类的友元没有继承性。当基类中有虚函数时,派生类只能是public继承方式,不能是private继承方式。

60 结构体 C++语言是从C语言发展而来的,面向对象程序设计中的类是结构化程序设计中结构体的进一步发展,为了使习惯于使用C语言结构体的编程人员能习惯使用C++语言中的类,C++语言中定义结构体和类概念基本相同,惟一不同之处是结构体中都是共有数据成员和共有成员函数,没有私有数据成员和私有成员函数。

61 对象 具有类类型的变量称作对象,或者说,对象是类的实例化。例1―5中base、d1和d2就分别是类Parent、Derived1和Derived2的对象。从上述的C++语言对结构体的定义,还可以说,具有类类型或结构体类型的变量称作对象,或者说,对象是类或结构体的实例化。

62 1.4 抽象类型和模板   在软件开发和维护过程中需要花费大量的人力和时间,如果能够重复利用以前开发完成的数据结构和算法模块,把它们有选择地组装在新的软件中,将可以大大减少需花费的人力和时间,这就是软件的复用问题。在C++中,实现软件复用的方法主要有抽象类型法和模板法。

63 抽象类型 抽象类型方法是类的定义中数据成员的类型使用抽象类型,当应用程序具体包含该类头文件前再对该抽象类型进行具体定义。如例1―3中的类Temperature可定义如下: class Temperature { private: elemtype high Temp elemtype low Temp; public:

64 Temperature(floath,floatl){highTemp=h;lowTemp=l;};
voidUpdateTemp(floattemp); elemtypeGetHighTemp(void)const; elemtypeGetLowTemp(void)const; }; 假设上述类Temperature的定义和实现放在文件Temperature.h中,当应用程序需要的精度为float时,则主程序包含该文件的方式为:

65 typedeffloatelemtype;
#include"Temperature.h" 当应用程序需要的精度为double时,则主程序包含该文件的方式为: typedef double elemtype;

66 1.4.2 模板 上述类Temperature定义的模板定义方法是: template<classelemtype> classTemperature { private: elemtypehighTemp elemtypelowTemp; public: Temperature(floath,floatl){highTemp=h;lowTemp=l;};

67 Void Update Temp(floattemp);
Elemtype GetHigh Temp(void) const; Elemtype GetLow Temp(void) const; }; 在调用程序中定义具体float类型或double类型对象的方法是: Temperature<float>temp; Temperature<double>temp;


Download ppt "第1章 C++面向对象程序设计要点 1.1 函数和函数参数 1.2 输入输出   1.3 类 1.4 抽象类型和模板."

Similar presentations


Ads by Google