C++面向对象程序设计 课件第3章_第1页
C++面向对象程序设计 课件第3章_第2页
C++面向对象程序设计 课件第3章_第3页
C++面向对象程序设计 课件第3章_第4页
C++面向对象程序设计 课件第3章_第5页
已阅读5页,还剩54页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

知识提要了解面向对象程序设计的概念掌握类的定义及使用掌握构造函数及析构函数的功能及定义了解拷贝构造函数、深浅拷贝掌握类中特殊成员的使用方法了解友元函数及友元类p13.1面向对象程序设计1.面向对象与面向过程的区别2.面向对象的基本概念对象类抽象p23.2类的定义类的定义形式p3class类名

//类的名字,任意合法的标识符,建议首字母大写{成员访问控制符://描述成员的访问权限,缺省为private

数据成员;//描述类的属性或特征成员访问控制符:

成员函数;//描述类的行为,也称为类的方法};

//类声明的末尾以分号结束public(公有访问权限)private(私有访问权限)protected(受保护访问权限)3.2类的定义例如,类Student定义如下:p4classStudent{public://公有访问权限 intGetAge()const;//GetAge()方法获得学生的年龄 doubleAvgScore();//AvgScore()方法求学生的平均成绩private://私有访问权限 stringstrName;//描述学生的姓名 intnAge;//描述学生的年龄 doublearrScore[4];//4个元素的数组,分别表示4门课的

考试成绩};3.2类的定义C++还提供了另外一种类的定义格式:用关键字struct替换class。如,用struct定义的类与class定义类的区别是,如果在struct定义类时省略访问权限限定符,则默认的访问权限是public。p5structTime{public: voidSetTime(); voidGetTime();private: inthour,minute,second;};成员的访问控制访问控制符有三个:publicprivateprotectedp6成员的访问控制1.public用public修饰的成员是公有成员,具有与类外交互的能力公有类成员可以直接在类的外部访问,公有成员是不隐藏的2.private用private修饰的成员称为私有成员,只能在类的成员函数或类的友元函数中访问,不能在类的外部直接访问实现了面向对象程序设计的一个重要思想:数据封装。3.protected用protected修饰的成员称为受保护的成员与私有成员一样,受保护的成员也只能在类的成员函数或友元函数中访问,不能在类的外部直接访问。p7数据的封装封装(encapsulation)是面向对象程序设计最重要的特征之一,它是指将数据和处理数据的函数封装成一个整体,以实现独立性很强的模块,避免外界直接访问对象属性而造成耦合度过高及过度依赖,同时也阻止了外界对对象内部数据的修改而可能引发的不可预知的错误。p8成员函数的实现成员函数的功能是通过程序代码实现的。实现成员函数就是定义成员函数,编写代码实现相应的功能。成员函数的实现有两种方法:在类的定义中实现在类外实现p9成员函数的实现1.在类中实现成员函数通常,功能简单,代码较少的成员函数可以在在类内实现在类内实现的、代码简单的成员函数将自动作为内联函数使用。p10classStudent{public: intGetAge()const//GetAge()方法获得学生的年龄 { returnnAge; } doubleAvgScore();//AvgScore()方法求学生的平均成绩private: stringstrName;

//描述学生的姓名 intnAge;//描述学生的年龄 doublearrScore[4];//存储4门课的考试成绩};成员函数的实现2.在类外实现成员函数如果成员函数的功能复杂,代码较多,则强烈建议在类外实现。在类外实现成员函数时需要在函数名前加类名及作用域解析运算符“::”,表示该函数属于哪个类。如果愿意,也可以把类外实现的成员函数声明为内联函数,只需要在函数首部的最前面加关键词inline即可。p11doubleStudent::AvgScore(){ doublesumScore=0; for(inti=0;i<4;i++) { sumScore+=arrScore[i]; } returnsumScore/4;}3.3使用类类是一种自定义数据类型,类一旦声明就可以像使用内置数据类型一样使用它。C++的目标是使得使用类与使用基本的内置数据类型尽可能相同。p12创建对象声明对象的基本格式p13类名

对象列表;Students1,s2;//实例化对象s1,s2创建对象当实例化对象时,编译器需要为对象分配内存空间,存储对象的成员。当为对象分配内存空间时,每个对象都分配独立的内存空间存储数据成员值。所有对象的成员函数的代码都存储在同一段内存空间中。不同的对象调用同名成员函数时,实际上调用的是同一段内存代码。

例3-1p14访问对象成员访问数据成员的语法格式访问对象的成员函数的语法格式在类的定义中为每个成员指定了相应的访问权限(默认访问控制符是private)。这些访问控制符限定了成员访问的“正确”位置。p15对象名.数据成员名;对象名.成员函数名();s1.nAge//对象s1的数据成员nAgeGetAge()//调用对象s1的成员函数intmain(){ Students1,s2; cout<<s1.nAge<<endl;//语法错误,私有数据成员不能在类外访问 cout<<s1.GetAge()<<endl;//正确,输出对象s1的年龄 return0;}以上程序无法通过编译,语法错误:error:'intStudent::nAge'isprivate.this指针类的成员函数在内存中存储时只有一个副本,对象s1和s2共享GetAge()函数的代码。那么,为什么s1.GetAge()得到s1的年龄,而s2.GetAge()得到的就是s2的年龄?不同的对象调用相同的函数却得到不同的结果,这是如何做到的呢?这要归功于this指针!p16this指针this指针是一种特殊的指针,是C++实现数据封装的一种机制,它将对象和对象所调用的成员函数联系在一起,使得从外部看来每个对象都拥有自己的成员函数。程序编译后,成员函数中包含this指针。如,GetAge()函数编译后的形式如下:例3-2p17intStudent::GetAge()const{ returnthis->nAge;}3.4构造函数构造函数可以完成声明对象时为对象进行初始化的任务。构造函数是类的一种特殊成员函数,构造函数在对象创建时被自动调用,实例化一个对象。如果没有为类显式地提供构造函数,编译器会自动提供一个默认构造函数。p183.4构造函数构造函数的格式要求:构造函数名与类名相同;构造函数没有返回值类型声明;构造函数中没有return语句;p19默认构造函数编译器提供的默认构造函数的定义形式如下如,类Student的默认构造函数为p20类名::构造函数名(){}Student::Student(){}自定义无参构造函数例3-3p21自定义带参数的构造函数创建对象时可以为对象传递参数,为对象的数据成员赋值,以完成对象的初始化工作。例如,为此,需要为类Student类定义带有两个参数的构造函数。构造函数声明如下:例3-4p22Students("Jason",18);Student(conststring&,int);Student::Student(conststring&name,intage){ strName=name; nAge=age;}自定义带参数的构造函数还可以用参数初始化表的形式为数据成员初始化。基本格式:例如,类Student的带参数的构造函数也可以用以下代码实现:p23类名::构造函数名(参数1,参数2,…,参数n):数据成员1(参数1),数据成员2(参数2),…,数据成员n(参数n){

函数体;}Student::Student(conststring&name,intage):strName(name),nAge(age){}自定义带参数的构造函数值得注意的是:一旦为类定义了构造函数,编译器将不再为类提供默认构造函数。所以,为类Student定义了带参数的构造函数之后,以下对象的声明是错误的:解决上述问题的方法是再为类显式地定义不带参数的构造函数,如p24Students;//语法错误error:nomatchingfunctionforcallto'Student::Student()'Student::Student(){}自定义带参数的构造函数可以根据需要定义多个重载的构造函数。例如,以下语句声明三个Student对象:想要保证这条语句通过编译,就需要定义三个构造函数,它们的函数原型如下:p25Students1("Jason",18),s2("Jason"),s3;Student::Student(conststring&,int);Student::Student(conststring&);Student::Student();委托构造函数为类Student定义多个构造函数重载多个构造函数时代码冗余量会很大。p26Student::Student(conststring&name,intage):strName(name),nAge(age){}Student::Student(conststring&name):strName(name),nAge(19){}委托构造函数C++11标准引用了委托构造函数(delegatingconstructor),有时也称为转发构造函数(forwardingconstructor),扩展了构造函数初始化数据成员的功能。首先定义一个接收2个参数的构造函数:然后,定义另一个构造函数时通过初始化表的形式调用已经定义的构造函数,即把数据成员初始化的工作“委托”给另一个构造函数:p27Student::Student(conststring&name,intage):strName(name),nAge(age){}Student::Student(conststring&name):Student(name,19){}委托构造函数使用委托构造函数时要注意以下两点:(1)不能同时用委托方式和显式初始化的方式为同一个数据成员赋值(2)委托构造函数不能在函数体中调用目标构造函数。p28classX{public:X(inta):data1(a){}

X():X(1){}//正确,委托构造函数X(int)为data1赋值为1X():data1(2){}//正确,使用显式初始化的方式为data1赋值X():X(1),data1(2){}//错误,同时使用委托方式为data1赋值为1、显式初始化的方式为data1赋值为2private:intdata1;};上述错误提示:mem-initializerfor'X::data1'followsconstructordelegation。classX{public:X(inta):data1(a){}X(){X(1);}//数据成员data1的值不能初始化为1private:intdata1;};含有对象成员的构造函数如果类中的数据成员是另外一个类的对象,称这种成员为对象成员。例如,类Date声明如下:类Student中有一个Date类型的数据成员iBirthday,类Student声明如下类Student的构造函数代码如下:p29classDate{public: Date(inty,intm,intd):nYear(y),nMonth(m),nDay(d){ }private: intnYear,nMonth,nDay;};classStudent{public: Student(string,int,int,int);//构造函数声明private: stringstrName;//描述学生的姓名 DateiBirthday;//描述学生的年龄};Student::Student(conststring&name,intyear,intmonth,intday):strName(name),iBirthday(year,month,day){}含有对象成员的构造函数例3-5访问成员对象的数据成员的方法是:对象名.成员对象名.数据成员名;如,s1.iBirthday.nYear;p30默认参数的构造函数带默认参数的构造函数的实现形式如下:p31类名::构造函数名(参数1=默认值1,参数2=默认值2,...,参数n=默认值n){

函数体;}Student(conststring&="",int=19);//构造函数声明Student::Student(conststring&name,intage):strName(name),nAge(age)//构造函数实现{}默认参数的构造函数使用默认参数的构造函数时要注意以下几点问题:(1)默认参数值在函数声明时指定,如果只有函数定义没有函数声明,则可以在定义函数时为参数指定默认值;(2)一旦为一个形参指定了默认值,这个参数右侧所有形参都必须指定默认值;(3)当重载了多个构造函数时,要避免调用的二义性;特别注意,不要被默认构造函数的隐式形式所误导。例如例3-6p32Students1("Jason",18);//正确,提供了实参值,声明对象s1Students2;//正确,使用默认参数,声明对象s2Students3{};//正确,使用默认参数,声明对象s3。Students4();//注意:该语句没有任何语法错误,但是它函数声明,声明返回值为Student类型的函数s4,不是声明对象s4。默认参数的构造函数可以根据需要只为构造函数的部分参数提供默认值,如构造函数也可以显式地调用,尤其是用new运算符生成一个对象时需要显式地调用构造函数实例化对象时可以使用列表初始化方式为对象的数据成员赋初值。如,p33Student(conststring&,int=19);//构造函数声明Student::Student(conststring&name,intage):strName(name),nAge(age)//构造函数实现{}Students1=Student("Jason",18);Student*pStu=newStudent("Ailsa",20);Student*pStu=newStudent;Students1{"Jason",18};Students2={"Jason",18};Student*pStu=newStudent{"Jason",18};3.5析构函数当对象的生命期结束时,编译器会自动调用析构函数撤消该对象。析构函数也是类的一个特殊的成员函数,析构函数的定义格式要求如下。析构函数名很特殊:在构造函数名前加“~”;析构函数没有返回值类型声明,也没有return语句;析构函数没有参数,不能重载。p343.5析构函数如果没有为类定义析构函数,编译器自动为类提供一个默认析构函数,这种默认的析构函数不执行任何操作。如果在对象消亡之前希望程序做一些事情,比如清理资源、计算当前活跃对象的数目等,程序员需要显式地定义析构函数,以完成相关的操作。如果创建对象时用new运算符为对象的数据成员申请了内存空间,当对象消亡时一定要用delete运算符回收相应的内存空间,否则将造成内存的泄露。例3-7p353.6拷贝构造函数如果已经声明了Student类型的对象s1,当声明Student类型的对象s2时可以用s1为s2初始化:当用一个对象为另一个对象赋值时,编译器将调用拷贝构造函数完成对象的赋值操作。p36Students1;Students2{s1};//或者写成Students2(s1)、Students2=s1;Students1("Jason",18);//创建对象s1Students2(s1);//调用拷贝构造函数3.6拷贝构造函数如果没有为类自定义拷贝构造函数,编译器将为类提供默认的拷贝构造函数完成对象的赋值有时,默认拷贝构造函数并不能完全胜任对象的拷贝工作,比如当用new运算符为对象的数据成员申请内存资源时,默认拷贝构造函数往往会导致一些错误发生拷贝构造函数的形式参数必须是类对象的引用,最好是常引用。p37拷贝构造函数的触发时机1.使用一个对象初始化另一个对象时。例如,2.函数调用时,用对象作为实参传递给函数形参时。例如,3.函数返回值是类对象,创建临时对象作为返回值时。例如,例3-8p38Students1;//声明对象s1Students2(s1);//声明对象s2,用s1初始化s2Students1;AvgScore(s1);StudentLargerAge(Students);//参数是Student类对象,函数返回值是Student类对象s3=s1.LargerAge(s2);深拷贝与浅拷贝让我们先看一个程序,看看该程序到底发生了什么,导致这样的结果的原因是什么,如何解决这个问题。例3-9例3-10p393.7对象数组声明对象数组的基本语法格式创建未被显式初始化的对象数组时,编译器会调用默认构造函数实例化这些对象。如果声明数组时为数组元素赋初值,需要调用构造函数初始化数组元素。如p40类名数组名[常量表达式];StudentarrStu[10];//声明具有10个Student类型的对象数组arrStuStudentarrStu[5]={{"Jason",18},{"Kevin",19},{"Cora",18},{"Alisa",20},{"Eva",18}};3.7对象数组数组arrStu的声明也可以写成如下形式:这种数组的定义需要结合拷贝构造函数,并且拷贝构造函数的参数是Student类对象的常引用也可以只对部分元素初始化。如,例3-11p41StudentarrStu[5]={ Student("Jason",18),Student("Kevin",19),Student("Cora",18),Student("Alisa",20),Student("Eva",18)};StudentarrStu[5]={Student("Jason",18),Student("Kevin",19),Student("Cora",18)};3.8数据共享数据共享的意义所有对象的某个数据成员的值都是相同的如果更新了某个对象的该数据成员的值,其他对象的该数据成员的值也相应地更新数据共享的方法静态数据成员p42静态数据成员用static关键字声明的数据成员可以被类的所有对象共享。静态数据成员是描述类的所有对象共同特征的数据成员,对于不同的类对象来说,静态数据成员的值是相同的。p43静态数据成员1.静态数据成员的声明格式2.静态数据成员的初始化p44static类型说明符

对象名;如,staticstringteacherName;类型说明符类名::静态数据成员=初值;如,stringStudent::teacherName="Kevin";静态数据成员3.静态数据成员的访问注意:在多线程程序中,需要为静态数据成员添加某种锁或设置访问规则,以避免出现资源竞争(racecondition)。p45类名::静态数据成员名;如,cout<<Student::teacherName<<endl;静态成员函数基本格式在类外实现静态成员函数时不需要添加static关键字同静态数据成员一样,静态成员函数也属于整个类,由类的所有对象共享,通常用类名和域限定符“::”对静态成员函数进行调用。p46static类型说明符函数名(参数列表);如,staticintGetStuCnt();静态成员函数例3-12设计静态成员函数不是为了在对象之间传递消息,仅仅是为了处理静态数据成员。在静态成员函数中通常只访问静态数据成员或其它静态成员函数。静态成员函数没有this指针,因此不能在静态成员函数中对非静态数据成员进行默认访问。p473.9数据保护关键字const也用于声明类中数据成员或成员函数,其目的是保护类中的数据不被更新。常数据成员静态常数据成员常成员函数常对象p48常数据成员用const声明常数据成员的格式是:常数据成员必须通过构造函数的参数初始化表的形式进行初始化,不能在构造函数的函数体内用赋值语句初始化。例3-13p49const类型说明符

数据成员;//或者类型说明符const数据成员;静态常数据成员如果类的所有对象共享某个数据成员,且该成员的值不允许变化,则可以把数据成员声明为静态常数据成员。C++规定,如果类的静态常数据成员是整型或枚举型,则可以直接在类定义中为其指定初值。如,p50classCircle{public: Circle();private: staticconstdoublePI;//PI是静态常数据成员};constdoubleCircle::PI=3.14;//在类外初始化classA{private:

staticconstintx=1;//直接在类内初始化静态常数据成员};常成员函数声明格式常成员函数用于保护对象的数据成员不被修改。如果不希望在函数内部改变数据成员的值,最好把该函数定义为常成员函数,这是一个好习惯,能够提高程序

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论