版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第十二章类与对象(一)C++程序设计——大模型思维与实践内容导航引言类的组合用类封装数据动态对象从面向过程到面向对象结构体简介对象指针大模型实践对象与函数大模型探究对象的构造与析构本章小结引言3面向过程编程在计算机科学发展的早期阶段,面向过程编程是一种主要的编程范式。它强调以过程和步骤为中心,通过将程序分解为一系列函数或子程序来解决问题。局限性:代码难以维护、复用性差和难以适应需求的变化等。面向对象编程面向对象编程是一种以对象为中心的编程范式,通过引入类和对象的概念,将数据和操作数据的方法封装在一起。提高了程序的模块化、可重用性和可维护性。封装封装将数据(成员变量)和操作这些数据的函数(成员函数)捆绑在一起,形成一个独立的模块——对象。信息隐藏:对象可以对外隐藏其内部实现细节,外部只能通过类提供的接口(公有成员函数)访问或操作数据,保护对象的内部状态,防止外部代码直接访问或修改对象的私有成员。确保对象的安全性和完整性。面向过程与面向对象4string类是典型的面向对象编程的范例数据的封装字符数组:string内部维护了一个字符数组来存储字符串的实际内容。这个数组对用户是隐藏的,用户不需要(也不应该)直接访问它。长度信息:string类还维护了一个表示字符串长度的变量。指针:string类还维护了用于begin()、rbegin()等函数返回的指针。函数的封装增(添加):append()、insert(),字符数组满了后,能自动扩充数组删(删除):erase()、pop_back()改(修改):replace()、operator[]
查(查询):find()、rfind()、find_first_of()string类的封装5cin用于处理标准输入的对象,封装了与输入流相关的底层细节,为用户提供了一个简洁且功能强大的接口来读取输入数据。数据的封装输入缓冲区:暂存从标准输入设备(如键盘)读取的数据。这个缓冲区对用户是隐藏的。流状态:用于指示输入操作的成功与否,以及是否遇到了错误(如读取失败、达到文件末尾等)。这些状态信息通过成员函数(如fail(),eof(),good()等)来访问。函数的封装>>:从输入流中读取数据并存储到指定的变量中。get():从输入流中读取一个字符或一行字符。getline():从输入流中读取一行字符。cin对象的封装我们的需求管理学生成绩,内容如下:如何在程序中表示这组学生信息?学号姓名性别数学英语编程101wangM859296102liM888378103chenF988976104qianF879599......
可选方案用二维数组表示该方案不可行,因为这些信息有不同的类型每一列用一个一维数组来表示,这种方法称为并联数组。要保证每位学生信息的正确性很难并联数组的解决方法intstudentId[30];//学号stringstudentName[30];//姓名charsex[30];//性别intmath[30];//数学intenglish[30];//英语intprogram[30];//编程学号姓名性别数学英语编程101wangM859296102liM888378103chenF988976104qianF879599......
数据的内存管理方式对数组维护时,易发生错位结构显得零散,不易管理拓展性差,添加新字段需要定义新数组101102103104......wanglichenqian......MMFF......85889887......92838995......96787699......数组的定义“类”类型的作用101wangM859296102liM888378103chenF988976104qianF879599学生1学生2学生3学生4希望的内存分配图“类”类型允许程序员把一些分量聚合成一个整体,用一个变量表示。一个类的各个分量都有名字,把这些分量称为成员(member)。由于类的成员可以是各种类型的(如int、string、char等),程序员能创建适合于问题的数据聚合。类中还可以定义各种成员函数对数据进行处理。类的使用123第3步:访问对象第1步:定义一个新的类型第2步:定义新类型的变量(对象)C++已提供string类等类型,若需处理字符串数据,我们可直接利用这一预定义类型,而无需额外定义。如需定义自己的数据聚合形式,或是满足特定需求的复杂数据结构时,就必须亲自设计并定义一个全新的“类”。定义了类之后,需利用类名创建该类的实例,即对象。例如,当我们想利用string类型处理文本时,可以这样声明对象:strings1,s2;。cin是C++标准库预先定义好的对象,可以直接使用,无需经历类的定义与对象的创建过程。通过对象,能够访问其成员变量、调用成员函数。cin>>s1;这行代码使用了cin对象和s1对象,将用户输入的数据存储至s1中。内容导航引言类的组合用类封装数据动态对象从面向过程到面向对象结构体简介对象指针大模型实践对象与函数大模型探究对象的构造与析构本章小结用类封装数据12定义语法格式class类名{
访问控制符:
数据成员;};声明类—封装数据关键字,用于声明类类的名称,提供唯一标识。指定访问权限。仅封装数据时指定为:public:意为公有成员,访问不受限制。后跟一个冒号,之后再跟类的成员类中声明的变量。放在类的定义末尾,表示类的定义已经结束。类是一种用户定义的数据类型,它封装数据成员(属性)和成员函数(方法)。本节仅讨论用类封装数据,因此,定义类时不包括成员函数。一对花括号,包围类的所有成员。13举例【例12-1】声明一个学生类,数据成员有:学号、姓名、性别、数学、英语和编程成绩。classStudent{public:intstudentId;stringstudentName;charsex;intmath;intenglish;intprogram;};声明类—封装数据类名访问控制符(公有成员)数据成员类定义结束符关键字形成一个类型声明的样板,是一个抽象的“虚”体用于生成变量(对象)但并未定义变量因而编译器不为其分配内存
注意:代码studentName=“zhang”;出错,因为不存在一个独立的studentName变量。如果希望给studentName赋值,必须先创建一个Student类的对象,然后才能通过该对象来访问和修改studentName成员14以一个类作数据类型定义的变量就是对象创建对象又称实例化,对象又称为实例。一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。对象的定义和普通的变量定义一样语法格式:类名对象名表={初始化列表};对象名表可以是简单的标识符,也可以是数组、指针等。例如:Studentstudent1,sArray[10],*sptr;对象建立后,对象占据内存,变成一个“实”体。会分配一块连续的空间,依次存放它的每一个分量。这块空间总的名字就是对象的名字。内部还有各自的名字。创建对象所有数据成员都为公有成员才可如此初始化举例Students={101,"wang",'M',85,92,96};15访问对象的成员
对象名.数据成员对象名:类的一个实例。数据成员:类中定义的变量。使用对象举例【例12-2】使用点运算符“.”访问和操作对象的成员。intmain(){Students={101,“wang",'M',85,92,96};cout<<s.studentName<<endl;inttotal=s.math+s.english+gram;cout<<total;return0;}不可写成cout<<studentName或cout<<Student.studentName16对象可以整体赋值:例如:Students1,s2={101,“wang",'M',85,92,96};s1=s2;对象不能整体输入,只能逐个成员输入。如:输入s1的内容可用:对象不能整体输出,只能逐个成员输出。如:输出s1的内容可用:使用对象cin>>s1.studentId>>s1.studentName>>s1.sex;cin>>s1.math>>s1.english>>gram;cout<<s1.studentId<<""<<s1.studentName<<""<<s1.sex<<"";cout<<s1.math<<""<<s1.english<<""<<gram;cin>>s1;及cout<<s1;错误17【例12-3】输入两个学生的学号、姓名和三门课成绩,输出总成绩较高学生的学号、姓名和总成绩。解题思路:定义一个包含学生信息的类,类中包含学号、姓名、共3个元素的成绩数组和总成绩4个数据成员。定义两个学生对象s1和s2。分别输入两个学生的学号、姓名和三门课成绩,并计算出总成绩存入对象的成员。比较两个学生的总成绩:如果学生1的总成绩高于学生2,输出学生1的学号、姓名和总成绩。如果学生2的总成绩高于学生1,输出学生2的学号、姓名和总成绩。如果二者总成绩相等,输出两个学生的学号、姓名和总成绩。使用对象18class
Student{public:
intid;
stringname;
intscores[3];
inttotal;};intmain(){
Students1,s2;cin>>s1.id>>
>>s1.scores[0]>>s1.scores[1]>>s1.scores[2];s1.total=s1.scores[0]+s1.scores[1]+s1.scores[2];cin>>s2.id>>;s2.total=0;
for(inti=0;i<3;i++){cin>>s2.scores[i];s2.total+=s2.scores[i];
}cout<<
"Thehigherscoreis:"
<<endl;
if(s1.total>s2.total)cout<<s1.id<<
","
<<
<<
","
<<s1.total<<endl;
else
if(s2.total>s1.total)cout<<s2.id<<
","
<<
<<
","
<<s2.total<<endl;
else{cout<<s1.id<<
","
<<
<<
","
<<s1.total<<endl;cout<<s2.id<<
","
<<
<<
","
<<s2.total<<endl;
}}使用对象数据成员可以为数组访问成员数组19【例12-4】输入10个学生的学号、姓名和3门课成绩,输出最高的总成绩。解题思路:定义一个包含学生信息的类,类中包含学号、姓名、3个元素的成绩数组和总成绩。定义对象数组s[10]。输入10个学生的学号、姓名和3门课成绩,并计算出总成绩存入对象的成员。循环查找最高的总成绩。对象数组语法格式类名数组名[n];//n为常量
vector<类名>数组名(n);将创建一个包含n个元素的对象数组,数组中的每个元素都是“类名”类的对象。通过数组下标可以访问和操作数组中的每个对象。20class
Student{public:
intid;
stringname;
intscores[3];
inttotal;};intmain(){
Students[10];
for(inti=0;i<10;++i){cin>>s[i].id>>s[i].name;s[i].total=0;
for(intj=0;j<3;++j){cin>>s[i].scores[j];s[i].total+=s[i].scores[j];
}
}
intmax=s[0].total;
for(inti=1;i<10;++i)
if(s[i].total>max)max=s[i].total;cout<<max<<endl;
return0;}对象数组访问数组元素的成员定义对象数组访问数组元素的成员数组21【例12-5】输入10个学生的姓名和3门课成绩,要求按照总成绩的高低顺序输出各学生的姓名和总成绩。解题思路:定义一个包含学生信息的类,类中包含姓名、3个元素的成绩数组和总成绩。定义对象数组s[10]。输入10个学生的姓名、3门课成绩,并计算出总成绩存入对象的成员。使用冒泡排序按照总成绩降序排列。循环输出所有学生的姓名和总成绩对象数组22class
Student{public:
stringname;
intscores[3];
inttotal;};intmain(){
Students[10];
intn=10,i,j,k;
for(inti=0;i<n;++i){cin>>s[i].name;s[i].total=0;
for(intj=0;j<3;++j){cin>>s[i].scores[j];s[i].total+=s[i].scores[j];
}
}for(inti=0;i<n-1;++i)for(intj=0;j<n-i-1;++j)if(s[j].total<s[j+1].total){
Studenttemp=s[j];s[j]=s[j+1];s[j+1]=temp;}cout<<
"Theorderis:\n";for(i=0;i<n;i++)cout<<s[i].name<<
""
<<s[i].total<<endl;return0;}对象数组冒泡排序23【例12-6】某班进行班长选举,有3个候选人wang、zhang、chen,每个同学只能投票选一人,要求编写一个统计选票的程序,输入每张选票上被选人的名字,最后按照得票数降序输出各人的得票,如果得票数相同,则按候选人姓名升序排列。解题思路:定义一个包含候选人信息的类,类中包含姓名、得票数。定义对象数组s[3],初始化姓名和得票数(初始为0)。输入被选人的姓名,然后与数组元素中的“姓名”成员比较,如果相同,就给这个元素中的“得票数”成员的值加1使用STL库的sort函数按照得票数降序、候选人姓名升序排列。循环输出所有候选人的姓名和得票数对象数组24#include
<iostream>#include
<algorithm>using
namespacestd;class
Candidate{public:
stringname;
intvotes;};//自定义比较函数boolcompare(Candidate
a,Candidate
b){
if(a.votes!=b.votes)
return
a.votes>b.votes;//按得票数降序
else
return
a.name<
b.name;//按姓名升序}
intmain(){
Candidates[3]={{"wang",0},{"zhang",0},{"chen",0}};对象数组数组的初始化可以省略内部的花括号,写成:Candidates[3]={"wang",0,"zhang",0,"chen",0}25
intn;cin>>n;
for(inti=0;i<n;++i){
stringvote;cin>>vote;
for(intj=0;j<3;++j){
if(s[j].name==vote){s[j].votes++;
break;
}
}
}sort(s,s+3,compare);//使用标准库的sort函数进行排序cout<<
"Theresultis:\n";
for(inti=0;i<3;++i){cout<<s[i].name<<
""
<<s[i].votes<<endl;
}
return0;}对象数组循环读取投票读取投票循环遍历候选人26sort函数位于头文件<algorithm>中,为高效的排序算法,排序速度比冒泡排序快得多。举例:int数组:intarr[]={5,2,9,1,5,6};sort(arr,arr+6);vector:vector<int>vec={5,2,9,1,5,6};sort(vec.begin(),vec.end());默认使用“<”运算符来比较元素,实现升序排序。数组或容器中的元素可能并不支持直接使用“<”运算符进行比较,或者需要实现降序排序。这时,需要为sort函数提供第三个参数,即自定义的排序规则。例中代码sort(s,s+3,compare)对s数组的3个元素排序。对象数组boolcompare(Candidate
a,Candidate
b){
if(a.votes!=b.votes)
return
a.votes>b.votes;//按得票数降序
else
return
a.name<
b.name;//按姓名升序}如果第一个参数应该在第二个参数之前,则返回true;否则,返回false内容导航引言类的组合用类封装数据动态对象从面向过程到面向对象结构体简介对象指针大模型实践对象与函数大模型探究对象的构造与析构本章小结从面向过程到面向对象28面向过程编程强调以过程和步骤为中心,通过将程序分解为一系列函数或子程序来解决问题。每个函数执行一个特定的任务。面向过程与面向对象的区别在前述选举班长编程中,针对Candidate的对象,程序员需要手动处理以下操作:选票统计:依据用户输入的候选人姓名,在候选人对象数组中查找该候选人,并将其得票计数递增1。排序机制:根据得票数的多少,对候选人进行降序排列。结果输出:循环遍历排序后的候选人列表,输出每位候选人的姓名及其得票数。局限性可维护性差:随着程序规模的增大,代码变得难以维护和理解。数据和操作分离:容易导致数据的不一致性,增加了调试和修正错误的难度。扩展性差:添加新的功能或修改现有功能需要修改大量代码。29面向过程与面向对象的区别面向对象编程以对象为中心,封装数据和对数据的操作。实现更高的代码模块化、可重用性和易维护性。改写选举班长编程:可以将候选人的所有数据和操作(统计选票和排序等算法)封装在一个称为“Election”的类中,程序员只需创建“Election”类的对象,输入候选人姓名,调用该对象的排序和输出操作,即可实现相应功能。针对“Election”类,可以方便地添加新的候选人或扩展新的功能,如增加投票限制、统计投票率等,能极大地降低开发难度,提升编程效率。tips:对于Election类,需要区分提供者与使用者。提供者专注Election类的开发和完善,使用者可达到轻松使用的目的。例如,C++系统提供了string,我们可使用它完成复杂的任务。30类定义语法格式class类名{public:公有数据成员或公有函数成员的定义;protected:保护数据成员或保护函数成员的定义;private:
私有数据成员或私有函数成员的定义;};类定义的完整格式public、protected、private:访问控制符。在类定义时,通过设置类的成员(包括数据成员和成员函数)的访问权限,来控制它们在程序中的可见性和使用权限。数据成员又称为属性,是类中声明的变量,表示对象的属性或状态。成员函数又称为方法,是类中声明的函数,用于操作类的对象,定义了类的行为。成员函数可以访问和修改类的成员变量。31举例【例12-7】定义一个汽车类,包括汽车品牌、出厂年份两个数据成员,以及设置汽车品牌及年份、显示汽车信息的两个成员函数。classCar{public:voidsetCar(stringb,inty){brand=b;year=y;}voiddisplay(){cout<<"Brand:"<<brand<<",Year:"<<year<<endl;}private:stringbrand;intyear;};类定义的完整格式访问控制符(公有成员)访问控制符(私有成员)数据成员成员函数类成员作用域为整个类,相当于类中的全局变量,且其声明位置不影响作用域范围,可以在所有成员函数中访问32成员函数作为类的一部分,定义了针对类中数据成员执行的操作。成员函数的定义、声明格式与非成员函数(全局函数)保持一致。成员函数可以放在类中定义,也可以放在类外。放在类中定义的成员函数为内联(inline)函数。前述Car类中的成员函数在类中定义。类内实现class类名{public://成员函数定义在类内
返回类型函数名称(){//函数体}};成员函数的定义与调用可以在类外定义成员函数,但必须同时在类内声明成员函数的原型。相当于在类内列了一个函数功能表,使得对类的成员函数的功能一目了然。实现方式如下:类外实现class类名{public://类内声明成员函数
返回类型函数名称();};//类声明在此结束//类外实现成员函数返回类型类名::函数名称(){//函数体}作用域标识符,放在类名和函数名称之间,表明后面的成员函数属于前面的那个类。33举例【例12-8】修改汽车类,在类内、类外分别实现两个成员函数。classCar{
stringbrand;intyear;public:voidsetCar(stringb,inty){brand=b;year=y;}
voiddisplay();};成员函数的定义与调用voidCar::display(){cout<<"Brand:"<<brand<<",Year:"<<year<<endl;}类内实现类外实现类内声明成员函数(不管类内还是类外)可直接访问类的成员(不用加点号等前缀)省略访问控制符,默认为private34通过对象访问成员语法格式:
对象名.数据成员对象名.成员函数(参数列表)数据成员:类中定义的变量。成员函数:类中定义的函数。成员函数的定义与调用举例main函数中使用点运算符“.”访问Car对象的公有成员。intmain(){CarmyCar1,myCar2;//创建Car类的对象
//使用点运算符调用成员函数myCar1.setCar("byd",2024);myCar2.setCar("wenjie",2025);myCar1.display();myCar2.display();return0;}对象的独立性:每个对象都拥有属于自己的数据成员,这些对象及其成员在内存中各自独立、互不影响。myCar1和myCar2两个Car类的对象,它们分别拥有独立的brand和year成员变量。成员函数的作用域:当调用myCar1.setCar("byd",2024)时,成员函数setCar内部的代码brand=b;year=y;实际上是将myCar1对象的brand和year成员赋值为"byd"和2024。此时,myCar2对象的成员变量并未发生任何变化。同理,对myCar2调用setCar("wenjie",2025)时,修改的是myCar2自身的数据成员,而不会影响myCar1。成员函数操作的始终是当前对象的数据成员。Brand:byd,Year:2024Brand:wenjie,Year:202535访问控制符主要有三种访问控制级别:public(公有)、private(私有)和protected(受保护)。每种访问控制方式适用于不同的场景和需求:public代表公有成员,可以在任何函数(包括类的成员函数和全局的普通函数)里直接访问和使用。public成员能够作为类与外界互动的接口,提供操作类对象的方法和访问类数据的途径。protected代表保护成员,本类的成员函数可以访问,派生类的成员函数也可以访问,但类外的函数(其它类的成员函数或者全局的普通函数)不允许访问。private仅可由本类的成员函数访问,对于类外的函数(其它类的成员函数或者全局的普通函数)不可见。通常用于保护类的内部实现细节,防止外部代码直接修改类的数据成员。为了访问private成员,通常通过public的成员函数(该函数能被外界访问)进行间接存取。访问控制符36public(公有)语法格式:
class类名{
public:
数据类型数据成员;
返回类型成员函数(参数列表);
};访问控制符当类的成员被声明为public时,任何函数(包括类的成员函数和全局的普通函数)都可以访问这些成员。37举例
【例12-9】定义一个Clock类,将其数据成员和成员函数设置为public。class
Clock{public:
intH,M,S;
voidSetTime(int
h,int
m,int
s)
{//成员函数可以访问public成员H=h;M=m;S=s;
}};访问控制符intmain(){
ClockmyClock;
myClock.H=3;//类外的main函数可以访问类的public数据成员myClock.M=10;myClock.S=20;
myClock.SetTime(1,2,3);//类外的main函数可以访问类的public成员函数
return0;}注意:public数据成员是最开放的访问方式,应慎重使用。例如:
在main函数中可执行myClock.S=200;将成员S设置为不合理的值。38private(私有)语法格式:
class类名{
private:
数据类型数据成员;
返回类型成员函数(参数列表);
};访问控制符声明为private的成员仅可由本类的成员函数访问,对于外界(在其它类的成员函数中或者全局的普通函数内通过对象访问时)不可见。为了访问这些被隐藏的数据,通常通过public的成员函数进行间接存取。39举例:【例12-10】定义一个Clock类,将其数据成员设置为private、成员函数设置为public。class
Clock{private:intH,M,S;public:
voidSetTime(int
h,int
m,int
s)
{//成员函数可以访问私有成员H=(h>=0&&h<24)?h:0;M=(m>=0&&m<60)?m:0;S=(s>=0&&s<60)?s:0;
}
voidShowTime()
{cout<<H<<":"<<M<<":"<<S<<endl;
}ClockAddTime(Clock
c2);};访问控制符Clock
Clock::AddTime(Clock
c2){//形参为Clock类型的变量
ClockT;//定义Clock类型的变量T.H=c2.H+H;
//其它代码略T.ShowTime();
returnT;
}intmain(){
ClockmyClock,c;myClock.SetTime(8,30,30);//错误,外界不可访问私有成员
//myClock.S=200;myClock.ShowTime();c.SetTime(4,0,0);Clockc1=myClock.AddTime(c);}成员函数可以访问本类的私有成员,包括本类其它对象的私有成员40说明在类的成员函数里,可以访问所有成员,包括私有、公有和保护成员。成员函数访问本实例的成员时,直接使用成员名,不需要使用点号运算符。外界的函数(如main函数)只能访问类的公有成员,并且需要使用对象和点号运算符如myClock.ShowTime()访问成员。每一种访问控制符都会对其后声明的所有成员变量(属性)和成员函数(方法)施加特定的访问权限,直到遇到下一个访问控制符为止。如果某个属性或方法前没有明确指定访问权限,C++默认将其视为private。这一默认行为强调了数据封装的理念,即默认情况下,类的内部状态对外隐藏,只能通过类提供的公共接口(成员函数)进行访问和操作。访问控制符在类中的出现次序没有严格规定,也可以根据需要出现多次。这种设计提供了极大的灵活性,允许开发者根据类的具体需求,精细地控制不同成员的访问权限。访问控制符41【例12-11】模拟银行账户的基本操作,包括存款、取款和查询余额等。由于账户名称、存款余额都不能由用户自由设置,需要将这些属性封装起来,并提供成员函数对这些属性进行操作。设计一个Account类,包含账户名称、存款余额两个属性,并实现:初始化、存款、取款、查询账户余额、显示账户的基本信息。class
Account{private:
stringname;
doublebalance;public:
//初始化账户
voidinit(string
accountName,double
b);
//查询余额
doublegetBalance();
//显示账户信息
voiddisplayAccountInfo();
//存款
voiddeposit(double
amount);
//取款
voidwithdraw(double
amount);};面向对象编程实例被声明为私有,只能通过公有成员函数访问和修改42void
Account::init(string
accountName,double
b){name=
accountName;balance=b;}double
Account::getBalance(){
returnbalance;}void
Account::displayAccountInfo(){cout<<
"AccountName:"
<<name<<endl;cout<<
"Balance:"
<<balance<<endl;}void
Account::deposit(double
amount){
if(amount>0){balance+=amount;cout<<
"Deposited:"
<<
amount
<<endl;
}
elsecout<<
"Invaliddepositamount."
<<endl;}void
Account::withdraw(double
amount){
if(amount>0&&amount<=balance){balance-=amount;cout<<
"Withdrew:"
<<
amount
<<endl;
}
elsecout<<
"Invalidwithdraw."
<<endl;}intmain(){
AccountmyAccount;//创建账户myAccount.init("wang",0);//初始化名称和余额myAccount.displayAccountInfo();//显示账户信息myAccount.deposit(500.0);//存款myAccount.displayAccountInfo();myAccount.withdraw(200.0);//取款myAccount.displayAccountInfo();cout<<
"CurrentBalance:"
<<myAccount.getBalance()<<endl;
return0;}面向对象编程实例保护私有成员,避免余额为负数面向对象编程实例数据成员:分子和分母两个整数。有理数类的操作:创建有理数的函数,用以设置有理数的分子和分母加法函数乘法函数输出有理数函数化简函数访问权限设计:数据成员是私有的化简函数是内部调用的函数,用户无需调用,因此也是私有的其他函数都是公有的
【例12-12】定义一个有理数类,该类能提供有理数的加和乘运算。要求保存的有理数是最简形式。如2/6应记录为1/3。设计考虑#include<iostream>usingnamespacestd;classRational{private:intnum;intden;voidReductFraction();//将有理数化简成最简形式public:voidcreate(intn,intd){num=n;den=d;ReductFraction();}voidadd(Rationalr2);voidmulti(Rationalr2);voiddisplay(){cout<<num<<'/'<<den<<endl;}};面向对象编程实例voidRational::add(Rationalr2){num=num*r2.den+r2.num*den;den=den*r2.den;ReductFraction();}voidRational::multi(Rationalr2){num=num*r2.num;den=den*r2.den;ReductFraction();}voidRational::ReductFraction(){inttmp=(num>den)?den:num;for(;tmp>1;--tmp)if(num%tmp==0&&den%tmp==0){num/=tmp;den/=tmp;break;}}intmain(){Rationalr1,r2;r1.create(1,2);r2.create(4,6);r1.add(r2);r1.display();r1.multi(r2);r1.display();}面向对象编程实例内容导航引言类的组合用类封装数据动态对象从面向过程到面向对象结构体简介对象指针大模型实践对象与函数大模型探究对象的构造与析构本章小结对象指针47对象指针存储的是对象的内存地址,可以通过对象指针访问对象的成员函数和成员变量对象指针的定义类名*指针名;//定义一个指向“类名”类型的指针;举例定义指向Car类对象的指针intmain(){CarmyCar;//创建对象
Car*p=&myCar;//定义指向对象的指针,并初始化为myCar的地址//等价于:
Car*p;p=&myCar;return0;}对象指针定义与使用48使用指针访问对象的成员使用解引用运算符(*)和点运算符(.):
(*对象指针).成员使用箭头运算符(->):
对象指针->成员举例使用指针访问Car类对象的公有成员intmain(){CarmyCar;Car*p=&myCar;
(*p).setCar("byd",2022);myCar.display();
(*p).display();p->display();return0;
}对象指针定义与使用三个语句等价注意加上圆括号49指针遍历对象数组创建对象数组。使用对象指针指向对象数组的首元素。通过指针和指针递增遍历数组中的每个对象。使用箭头运算符->访问对象的成员。对象数组的指针举例【例12-13】使用对象指针遍历Car对象数组。intmain(){//创建包含三个Car对象的对象数组CarmyCar[3];//用指针指向对象数组Car*p=myCar;for(inti=0;i<3;i++){p->setCar("byd",2020+i);//调用成员函数p->display();//调用成员函数p++;//指针指向下一个对象}return0;}运行结果:Brand:byd,Year:2020Brand:byd,Year:2021Brand:byd,Year:2022this指针在C++中,类的成员函数常常需要引用调用它的那个对象实例,对此,C++引入了一个隐式的、系统预定义的this指针来实现。this指针指向当前正在操作的对象,其本质上存储了当前对象的内存地址。当一个对象调用其成员函数时,编译器先将该对象的地址赋给this指针,然后调用成员函数。在大多数情况下,并不显式地使用this指针来引用对象的成员,但在某些特定场景下,this指针的显式使用变得非常必要,例如:重载运算符以实现对象的链式赋值需要区分成员变量和同名的函数参数51注意:this指针不是调用对象的名称,而是指向调用对象。this的值不能改变,它总是指向当前调用对象。举例【例12-14】使用this指针区分函数参数与成员变量。this指针classCar{private:stringbrand;intyear;public:voidsetCar(stringbrand,intyear){
this->brand=brand;this->year=year;}};通过这一机制,成员函数能够清晰地识别并访问所属对象的数据成员内容导航引言类的组合用类封装数据动态对象从面向过程到面向对象结构体简介对象指针大模型实践对象与函数大模型探究对象的构造与析构本章小结对象与函数53传递方式值传递将对象的副本传递给函数,函数中对形参对象的修改不会影响实参对象。适用于对象较小,且不需要在函数中修改原对象的情况。引用传递将对象的引用传递给函数,函数中对形参对象的修改会影响实参对象。效率高,因为不会创建对象的副本。指针传递通过传递对象的地址,使函数能够操作对象的内存,可以直接修改原对象。对象指针作为函数的参数传递,并通过箭头运算符->操作对象的成员。对象作为函数参数尽管对象和数组一样也由许多分量组成,但对象的值传递和普通内置类型一样。它将实际参数中的每个分量复制到形式参数的每个分量中。54对象作为函数参数值传递引用传递指针传递【例12-15】函数参数传递的比较函数内部直接修改形参对象的数据成员intmain(){
Students={101,"wang",'M',85};modify1(s);cout<<s.math<<endl;//输出85modify2(&s);cout<<s.math<<endl;//输出90modify3(s);cout<<s.math<<endl;//输出95
return0;}class
Student{public:
intstudentId;
stringstudentName;
charsex;
intmath;};voidmodify1(Student
s1){s1.math+=5;}voidmodify2(Student*p){p->math+=5;}voidmodify3(Student&stu){stu.math+=5;}55对象作为函数参数voidmodifyCar1(Carcar){//修改副本,不影响函数实参car.setCar("changan",2020);}voidmodifyCar2(Car&car){//修改引用,影响函数实参car.setCar("qirui",2021);}voidmodifyCar3(Car*p){//通过指针修改,影响函数实参p->setCar("weilai",2024);}值传递引用传递指针传递【例12-16】函数参数传递的比较函数内部调用形参对象的成员函数修改其属性intmain(){CarmyCar;myCar.setCar("byd",2022);cout<<"-----Car的原始信息:"<<endl;myCar.display();modifyCar1(myCar);cout<<"-----传值:"<<endl;myCar.display();modifyCar2(myCar);cout<<"-----传引用:"<<endl;myCar.display();modifyCar3(&myCar);cout<<"-----传指针:"<<endl;myCar.display();return0;}Brand:byd,Year:2022Brand:byd,Year:2022Brand:qirui,Year:2021Brand:weilai,Year:202456对象作为函数的返回值CarmodifyCar1(Carcar){car.setCar("changan",2020);//修改副本,不影响函数实参returncar;}intmain(){CarmyCar;myCar.setCar("byd",2022);cout<<"-----Car的原始信息:"<<endl;myCar.display();CarnewCar=modifyCar1(myCar);cout<<"-----传值并获得返回值:"<<endl;newCar.display();return0;}【例12-17】:返回对象(值)修改例12-16的modifyCar1函数,使得它返回修改后的carC++中的函数可以返回对象,函数的返回值是对象的副本。Brand:byd,Year:2022Brand:changan,Year:2020内容导航引言类的组合用类封装数据动态对象从面向过程到面向对象结构体简介对象指针大模型实践对象与函数大模型探究对象的构造与析构本章小结对象的构造与析构58构造函数(constructor)是一个特殊的成员函数,用于在对象创建时对其进行初始化。构造函数的主要任务是为对象的数据成员分配初始值,以确保对象在使用之前处于有效状态。构造函数的必要性:【例12-18】对象创建时被随机初始化:创建对象后调用SetTime函数之前,myClock对象的值不合法(不存在时分秒为负数的时间值),如果忘记调用SetTime函数,可能带来严重后果。构造函数的概念myClock初始状态H:随机值M:随机值S:随机值class
Clock{private:
intH,M,S;public:
voidSetTime(int
h,int
m,int
s);
voidShowTime();}
ClockmyClock;myClock.ShowTime();myClock.SetTime(8,30,30);myClock.ShowTime();59构造函数在对象被创建时自动调用,用于初始化对象的成员变量定义格式如下:class类名{public:
类名(参数列表);//构造函数声明}构造函数的特点与类同名:构造函数的名字必须与类名完全一致,这是它的最大标识。没有返回类型:构造函数不需要也不能有返回类型(包括void)。通常声明为public,否则在类外的函数(其它类的成员函数或者全局的普通函数)里不能创建该类的对象。自动调用:构造函数在对象创建时被自动调用,无需且不能直接调用它。默认构造函数:如果未定义任何构造函数,系统会提供一个默认构造函数,即没有任何参数的构造函数,并将数据成员初始化为随机值。构造函数的概念class
Clock{private:
intH,M,S;public:Clock(){H=0;M=0;S=0;}};60构造函数的概念classClock{private:intH,M,S;public://以下函数为默认构造函数,如果未定义任何构造函数,系统将自动生成一个与以下函数功能一样的构造函数,H、M、S被初始化为随机值Clock(){}voidSetTime(inth,intm,ints){……}voidShowTime(){cout<<H<<":"<<M<<":"<<S<<endl;}};【例12-19】默认构造函数intmain(){ClockmyClock;myClock.ShowTime();//显示随机值return0;
}输出随机值:-858993460:-858993460:-858993460成员变量为随机值Clock(){cout<<"构造函数被调用"<<endl;}观察被自动调用Clock(){H=0;M=0;S=0;}自定义默认构造函数初始化61构造函数重载的概念前述无参构造函数不接受任何参数,将对象的成员变量初始化为随机值或默认值。构造函数能接纳一个或多个参数,这些参数在对象创建时传入,根据具体的需求初始化对象的成员变量。同一个类中,可以定义多个构造函数,它们具有相同的名字(都使用类名作为函数名),但参数列表不同,称为构造函数的重载。通过重载构造函数,我们能够为对象的创建提供多样化的初始化手段,满足不同需求。语法格式class类名{public:
类名();//无参构造函数(默认构造函数)
类名(参数列表1);//有参构造函数1
类名(参数列表2);//有参构造函数2}如果定义了多个构造函数,创建对象时,系统会根据传递的参数类型和个数从中选择最匹配的构造函数完成对象的初始化。如果没有定义相应参数的构造函数,则会出现编译错误。构造函数的重载62构造函数的重载classRectangle{private:intwidth;intheight;public:Rectangle(){width=0;height=0;}Rectangle(intw,inth){width=w;height=h;}Rectangle(intside){width=side;height=side;}intgetArea(){returnwidth*height;}//成员函数:计算面积};【例12-17】构造函数重载重载1:无参构造函数,宽和高设置为0重载2:有参构造函数,指定宽和高重载3:有参构造函数,指定宽和高相等63构造函数的重载intmain(){//调用无参构造函数Rectanglerect1;//调用有参构造函数1Rectanglerect2(10,20);//调用有参构造函数2Rectanglerect3(15);//错误,无3个参数的构造函数Rectanglerect4(10,20,30);【例12-17】构造函数重载可以定义多个构造函数,在建立对象时根据参数类型和个数来调用相应的构造函数。如果相应的构造函数没有定义,则出错。class
Rectangle{private:
intwidth;
intheight;public:Rectangle();Rectangle(int
w,int
h);Rectangle(int
side);
intgetArea();};X任何对象在创建时都必须调用构造函数!64构造函数的重载【例12-17】构造函数重载//定义数组,调用无参(默认)构造函数RectanglerectArray1[4];//定义数组,使用3个Rectangle常量进行初始化RectanglerectArray2[5]={Rectangle(),Rectangle(5,10),Rectangle(3)};return0;}注意:1.不是直接调用构造函数,而是创建对象常量2.初始化了前三个元素,后两个使用默认构造函数创建对象class
Rectangle{private:
intwidth;
intheight;public:Rectangle();Rectangle(int
w,int
h);Rectangle(int
side);
intgetArea();};65构造函数的重载【例12-18】用户定义了构造函数后,系统不再提供默认构造函数classClock{private:intH,M,S;public:Clock(inth,intm,ints){H=(h>=0&&h<24)?h:0;M=(m>=0&&m<60)?m:0;S=(s>=0&&s<60)?s:0;}voidShowTime(){cout<<H<<":"<<M<<":"<<S<<endl;}};ClockmyClock1myClock1.ShowTime();ClockcArr[5];cArr[0].ShowTime();ClockmyClock2(8,30,30);myClock2.ShowTime();没有提供参数没有提供参数需要增加一个无参构造函数66构造函数的重载最佳实践:为Clock类的构造函数定义默认形参值:可如下方式创建对象:Clock(int
h=0,int
m=0,int
s=0)
{H=(h>=0&&h<24)?h:0;M=(m>=0&&m<60)?m:0;S=(s>=0&&s<60)?s:0;
}
Clockc1;c1.ShowTime();
Clockc2(8);c2.ShowTime();
Clockc3(8,30);c3.ShowTime();
Clockc4(8,30,30);c4.ShowTime();67构造函数还可以通过使用初始化列表来高效地完成数据成员的初始化语法格式类名(参数列表):成员变量1(参数),成员变量2(参数),...{}尤其适用于需要在对象创建时立即初始化的成员,例如常量、引用等。构造函数的初始化列表举例【例12-19】对普通数据成员初始化。classCar{private:stringbrand;intyear;public:Car(stringb,inty):brand(b),year(y){}voiddisplay(){cout<<"Brand:"<<brand<<",Year:"<<year<<endl;}};Car(stringb,inty){brand=b;year=y;}初始化列表位于构造函数声明的冒号:之后,并紧跟一对括号()intmain(){Carcar("byd",2022);car.display();return0;}等价于68构造函数的初始化列表【例12-20】对类的常量和引用成员初始化。classA{private:constintSIZE;double&ref;public:
A(intsize,double&r):ref(r),SIZE(size){}};intmain(){doublevalue=3.14;Aa(5,value);return0;}常量成员和引用成员不能在构造函数体内进行赋值A(intsize,doubl
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 萨都剌诗词书画赏析
- AI在农业工程中的应用
- 2025-2026月考试卷八年级数学上学期期中模拟卷(沪教版)(考试版A4)
- DB63∕T 2544-2026 公路工程施工安全检查技术指南
- 2026年小学语文高年级阅读指导
- 2026年幼儿园小班教学策略与方法研究
- 2026年理发店活动设计方案
- 2026年餐厅餐具消毒流程管理规范标准
- 2026年中班下学期环保工作计划
- 2026年焊工实训方案及流程
- 建筑工程的毕业论文
- 辽河油田考勤管理制度
- 斜视教学课件
- 苏教版高一下册数学必修第二册-第14章统计章末复习【含答案】
- 2025年全国统一高考数学试卷(全国二卷)含答案
- 全渠道营销方案
- 学生会融媒体工作报告
- 【KAWO科握】2025年中国社交媒体平台指南报告
- 公安情报学试题及答案
- 《珊瑚礁的生态系统》课件
- 早产儿经口喂养临床实践专家共识(2025) 2
评论
0/150
提交评论