资源描述
C+程序设计类与对象第章1C/C+程序设计教程C+程序设计类与对象第章2第1章 类和对象 应该重点掌握以下内容:面向对象程序设计的基本特点 类和对象的定义和使用 构造函数和析构函数 拷贝构造函数和堆对象、对象数组 友元函数和友元类C+程序设计类与对象第章3第1章 类和对象 1.1 面向对象程序设计概述 1.2 面向对象程序设计的基本概念 1.3 类和对象的定义 1.4 构造函数和析构函数 1.5 类和对象的进一步应用 1.6 静态成员 1.7 友元函数和友元类 1.8 string类 1.9 综合应用实例C+程序设计类与对象第章41.1 面向对象程序设计概述一、什么是面向对象程序设计 面向对象程序设计(Object Oriented Programming-简称OOP)是软件系统设计与实现的新方法,这种新方法即吸取了结构化程序设计的全部优点,又考虑了现实世界与面向对象空间的映射关系,所追求的目标是将现实世界的问题求解尽可能的简单化。二、面向对象程序设计的基本思路 例如:生产一台电脑C+程序设计类与对象第章51.2 面向对象程序设计的基本概念 1.2.1 类 1.2.2 对象1.2.3 封装(encapsulation)与数据隐藏1.2.4 继承(inheritance)1.2.5 多态性(polymorphism)1.2.6 消息C+程序设计类与对象第章61.2.1 类 一、现实世界与面向对象系统对应关系 二、什么是类1.类是一种复杂的数据类型,它将不同类型的数据和与这些数据相关的操作封装在一起。类是对现实世界客观事物的抽象。2.两方面的要素:属性(atrribute)-数据 行为(behavior)-数据的操作(函数)C+程序设计类与对象第章71.2.2 对象一、什么是对象 对象是类的一个实体,又称为实例 二、对象与类的关系三、对象与对象之间消息传递 对象对象1对象对象2对象对象3对象对象4消息消息消息消息人类人类姓名姓名年龄年龄 身高身高体重体重走路走路学习学习说话说话开车开车数据抽象数据抽象小小王王小小李李小小张张人类人类的对象的对象C+程序设计类与对象第章81.2.3 封装与数据隐藏 一、什么是封装 封装是指将对象的数据和与这个数据有关的操作放在一起。各个对象之间相互独立,互不干扰。对象只留少量的接口,以便与外部联系。从外部看对象就像一个“黑匣子”,数据和方法是隐蔽的、看不见的。当用户使用对象时,不必知道对象的具体实现细节,只需要根据对象提供的外部接口访问对象即可。二、封装应具有以下几个特点 具有一个清楚的边界,私有成员封装在内部,外部不可以访问 提供必要的接口 对象内部的数据和方法是受封装外壳保护的,其它对象不能直接使用。主机主机主板主板显卡显卡声卡声卡内存内存数据传输线数据传输线电源线电源线USB接口接口串行接口串行接口电源开关电源开关 内内部部私私有有成成员员与与外外部部接接口口C+程序设计类与对象第章91.2.4 继承 一、继承概念的提出例如:已定义电视机类,现需要定义彩色电视机类解决方法:1.重新设计 2.继承:继承原来电视机的属性和方法,再添加彩色电视机自己特有的新的属性和行为 二、继承的概念 C+提供的继承机制提供了类之间相互关系的解决方案,使某个类可以继承另外一个类的特征和能力。使用继承符合人们对事物的认识和叙述,大大简化了对问题的描述,提高了程序的可重用性,从而提高了程序设计、修改、扩充的效率,实现软件重用(software reusability)。C+程序设计类与对象第章101.2.5 多态性(polymorphism)一、多态性的概念 多态性:描述的是同一个消息可以根据发送消息对象的不同采用不同的行为方式。例如:学校的上课铃响,不同班级的学生进入不同的教室学习,不同的老师进入不同教室开始讲课,不同的对象会作出不同的响应。可以看到学生和教师在接受到同一消息(上课铃声)时,执行不同的操作,这就是多态的表现。二、C+支持多态 静态多态性 动态多态性 C+程序设计类与对象第章111.2.6 消息 面向对象技术的封装使得对象相互独立,各个对象要相互协作实现系统的功能则需要对象之间的消息传递机制。消息是一个对象向另一个对象发出的服务请求,进行对象之间的通信。也可以说是一 个 对 象 调 用 另 一 个 对 象 的 方 法(M e t h o d)或 称 为 函 数(Function)。C+程序设计类与对象第章12面向过程与面向对象程序设计方法的比较一、面向过程程序设计方法 程序=数据结构+算法二、面向对象程序设计方法的规律是:对象=(数据结构+算法)程序=对象+对象+对象+对象C+程序设计类与对象第章131.3 类和对象的定义 一、类定义组成 数据成员:说明类的属性 成员函数(又称函数成员):对类数据成员操作的类内函数,又称为方法。二、类定义的一般格式为 class 类名 public:数据成员和成员函数实现 protected:数据成员和成员函数实现 private:数据成员和成员函数实现 ;C+程序设计类与对象第章141.3.1 类的定义说明:(1)定义一个类时,使用关键字class;类名必须是一个合法的变量名,C+中习惯用C或T开头,例如CStudent;(2)一个类包括类头和类体两部分。class ,称为类头(class head);(3)大括号中定义的是类的数据成员和成员函数,称为类体(class body)。类定义结束要用“;”结束;(4)关键字public(公有)、protected(保护)和private(私有)称为成员访问限定符(mumber access specifier)。用访问限定符声明了各个数据成员和成员函数的访问权限。C+程序设计类与对象第章151.3.1 类的定义三、访问限定符 公有(public):提供了与外界的接口功能。公有成员可以被本类中的成员使用和访问,还可以被类作用域内的其他外在其他函数使用。私有(private):封装在类内部的,只能被该类的成员和该类友元函数或友元类访问,任何类以外的函数对私有成员的访问都是非法的。保护(protected:访问权限介于私有和公有中间,类的成员可以访问,类以外的其他函数不能访问保护成员。不同访问权限关系图 类类Aclass外部外部私有成员私有成员private保护成员保护成员protected公有成员公有成员public继承类继承类友元友元class外部外部C+程序设计类与对象第章161.3.1 类的定义例:定义一个日期类CdateCdate-year:int-month:int-day:int+Setdate(y:int,m:int,d:int):void+Isleapyear():int+Print():void C+程序设计类与对象第章17#include class Cdate/使用关键字class声明一个类,类名为Cdate public:/以下是公有成员函数 void Setdate(int y,int m,int d)/设置日期 year=y;month=m;day=d;/以上3行是函数的实现int Isleapyear()/判断日期是不是闰年 return(year%4=0&year%100!=0)|(year%400=0);void Print()/定义一个公有成员函数Print()输出日期 coutyear-month-dayendl;private:/以下是私有成员 int year,month,day;/定义3个整型变量,分别表示日期的年月日;C+程序设计类与对象第章181.3.2 成员函数的定义类的成员函数也是函数的一种,它的与以前介绍的普通函数用法基本一样。成员函数由函数头和函数体组成,函数头包括函数名、函数的参数及函数的类型。函数体主要说明函数实现的功能。成员函数与一般函数的区别在于:它属于类的成员,出现在类体中,是类的一部分。使用类的成员函数时,要考虑类成员函数的访问权限。类的成员函数可以在类体中定义,也可以在类体中声明类成员函数,成员函数的实现在类外定义。C+程序设计类与对象第章192022-12-1191 在类内定义成员函数#include class Cdate public:void Setdate(int y,int m,int d)/在类内定义成员函数 year=y;month=m;day=d;int Isleapyear()return(year%4=0&year%100!=0)|(year%400=0);void Print()coutyear-month-dayendl;private:int year,month,day;date1,date2,date3;说明:在类内定义的成员函数默认的是内联函数(inline函数)。一般把规模比较小的成员函数在类内定义。C+程序设计类与对象第章202022-12-1202 在类外定义成员函数 通常情况下 C+程序员习惯在类体中只定义数据成员以及成员函数的声明,而将成员函数的实现写在类外。因此类的定义由两部分组成:声明部分 声明类的数据成员类型和成员函数的参数类型、个数以及返回值类型。实现部分 对说明类成员函数的具体实现C+程序设计类与对象第章212022-12-1212 在类外定义成员函数一、定义的格式 /类声明部分class 类名 public:private:Protected:数据成员类型 数据成员名;数据成员类型 数据成员名;函数类型 成员函数名(参数表);函数类型 成员函数名(参数表);/类实现部分函数类型 类名:成员函数名(参数表)/实现C+程序设计类与对象第章222022-12-1222 在类外定义成员函数说明:(1)在类声明部分,包括数据成员的定义和成员函数的声明。(2)成员函数的声明主要说明函数的类型,函数的参数个数及类型,并用“;”结束。(3)函数实现部分是对在类中定义的成员函数完成功能的实现,包括函数头和函数体。在类体中直接定义函数时,不需要在函数名前面加上类名,因为函数属于哪一个类是不言而喻的。但成员函数在类外定义时,必须在函数名前面加上类名,给与限定(qualifed),“”是作用域限定符(field qualifier)或称作用域运算符,用它声明函数是属于哪个类的。如果在作用域运算符“”的前面没有类名,或者函数名前面既无类名又无作用域运算符“”,则表示display函数不属于任何类,该函数不是成员函数,而是全局函数,即非成员函数的一般普通函数。C+程序设计类与对象第章232022-12-1232 在类外定义成员函数例:Cdate类定义/Cdate类声明部分class Cdate public:/函数声明 void Setdate(int y,int m,int d);/或void Setdate(int,int,int);int Isleapyear();void Print();private:int year,month,day;date1,date2,date3;/Cdate类成员函数的实现void Cdate:Setdate(int y,int m,int d)year=y;month=m;day=d;int Cdate:Isleapyear()return(year%4=0&year%100!=0)|(year%400=0);void Cdate:Print()coutyear-month-dayendl;C+程序设计类与对象第章241.3.3 类对象的定义 1先声明类类型,然后定义类对象class 类名 数据成员和成员函数实现;class 类名 对象列表说明:(1)class 可选,可以写,也可以省略(2)类名是定义好的类。(3)对象列表的格式为:对象名,对象名,对象名。定义多个对象时,各对象名中间用逗号隔开。如:class Cdate date1,date2,date3;/Cdate是已经声明的类类型 Cdate date1,date2,date3;/建议使用的方法。C+程序设计类与对象第章252在声明类类型的同时定义对象class 类名 数据成员和成员函数实现 对象列表;例:#include class Cdate/使用关键字class声明一个类,类名为Cdate public:/以下是公有成员函数 void Setdate(int y,int m,int d)/设置日期 year=y;month=m;day=d;/以上3行是函数的实现 int Isleapyear()/判断日期是不是闰年 return(year%4=0&year%100!=0)|(year%400=0);void Print()/定义一个公有成员函数Print()输出日期 coutyear-month-day 数据成员名 /“-”是指向运算符 对象指针名-成员函数名 例如:Cdate d,*p;/定义对象d和对象指针变量p p=&d;/初始化对象指针p p-Print();/通过指向对象的指针和指向运算符访问对象中的成员C+程序设计类与对象第章282022-12-1281.3.4 对象成员的访问(3)通过对象的引用变量访问对象中的成员 如果为一个对象定义了一个引用变量,它们是共占同一段存储单元的,实际上它们是同一个对象,只是用不同的名字表示而已。因此完全可以通过引用变量来访问该对象中的成员。通过对象的引用变量访问该对象中的成员的格式与通过对象名访问对象中的成员的形式一样。例如:Cdate d1;/定义对象d1 Cdate&d2=d1;/定义引用变量d2,并初始化之为d1 d2.Print();/引用变量访问对象中的成员,输出日期C+程序设计类与对象第章292022-12-129/Cdate.h 在头文件中声明日期类class Cdate public:void Setdate(int y,int m,int d);int Isleapyear();void Print();private:int year,month,day;/Cdate.cpp 在与类定义头文件同名的cpp文件中定义成员函数实现部分#include#include Cdate.h /包含类定义的头文件,否则出错void Cdate:Setdate(int y,int m,int d)year=y;month=m;day=d;int Cdate:Isleapyear()return(year%4=0&year%100!=0)|(year%400=0);void Cdate:Print()coutyear-month-day Setdate(2000,4,2);/通过指针访问成员通过指针访问成员 d4.Setdate(2006,12,1);/通过引用访问成员通过引用访问成员 d1.Print();p-Print();d4.Print();【例例1.11.1】一个完整的一个完整的CdateCdate类的应用程序。类的应用程序。程序运行结果:程序运行结果:1999-12-302000-4-22006-12-1C+程序设计类与对象第章302022-12-130d1.Setdate(1999,12,30)&d2yearmonth dayyear=1999month=12day=13yearmonth dayd1d2d4d3pp-Setdate(2000,4,2)yearmonth dayyear=2000month=4day=2year=2006month=12day=1d4.Setdate(2006,12,1).Setdate(2006,12,1);d1.Print()d4.Print()p-Print()屏幕输出结果:1999-12-302000-4-22006-12-1定义变量并为定义变量并为变量分配空间变量分配空间调用类成员函数调用类成员函数Setdate()初始()初始化类的数据成员化类的数据成员调用类成员函数调用类成员函数Print()输出()输出日期日期 例例1.11.1程序执行过程程序执行过程 C+程序设计类与对象第章312022-12-131【例例1.21.2】封装一个学生类,学生的信息包括学生的姓名、学号、性别、年龄和封装一个学生类,学生的信息包括学生的姓名、学号、性别、年龄和3 3门的成绩。可以设置学生的各类信息,统计该学生的平均成绩并输出学生信息。门的成绩。可以设置学生的各类信息,统计该学生的平均成绩并输出学生信息。CStudent-name20:char-id:int-sex:char-age:int-grad3:float+setinfo(na:char*,i:int,s:char,a:int):void+void setgrad(a:float,b:float,c:float):void+avg():float+display():voidCstudentCstudent的类图的类图 C+程序设计类与对象第章322022-12-132/CStudent.h 这是头文件,在此文件中进行类的声明class CStudent /类声明 public:void setinfo(char*,int,char,int);void setgrad(float,float,float);float avg();void display();private:char name20;int id;char sex;int age;float grad3;/CStudent.cpp在此文件中进行函数的定义#include#include#include CStudent.h/漏写此行编译通不过void CStudent:setinfo(char*na,int i,char s,int a)strcpy(name,na);id=i;sex=s;age=a;void CStudent:setgrad(float a,float b,float c)grad0=a;grad1=b;grad2=c;float CStudent:avg()return(grad0+grad1+grad2)/3;void CStudent:display()coutid:idendl;coutname:nameendl;coutsex:sexendl;coutage:ageendl;/main.cpp 主函数模块主函数模块#include#include CStudent.h/将类声明头文件包含进来将类声明头文件包含进来int main()CStudent stud1;/定义对象定义对象stud1.setinfo(张三张三,2006102,m,18);stud1.setgrad(79,98,87);stud1.display();cout 平均成绩:平均成绩:stud1.avg();return 0;运行结果:运行结果:id:name:张三:张三sex:mage:18平均成绩:平均成绩:88C+程序设计类与对象第章331.3.5 类对象的内存分配 C+中用同一内存空间来存放这个同种类对象的成员函数代码,当去调用某个对象的成员函数时,都去调用这个公用的函数代码。C+中用this指针来区分是哪个对象的成员函数。数据成员数据成员对象对象1数据成员数据成员对象对象2数据成员数据成员对象对象n公用成员函数代码公用成员函数代码C+程序设计类与对象第章342022-12-1341.3.6 this指针 一、问题的提出 每个对象中的数据成员都分别占有存储空间,而成员函数都是同一代码段。如果对同一个类定义了n个对象,那么,当不同对象的成员函数引用数据成员时,怎么能保证引用的数据成员是属于哪个对象呢?二、this指针 C+除了接受原有的参数外,还隐含的接受了一个特殊的指针参数,这个指针称为this指针。它是指向本类对象的指针,它的值是当前被调用的成员函数所在的对象的起始地址,既this指针指向当前对象。C+程序设计类与对象第章352022-12-135【例1.13】设计矩形类CRect,说明this指针的用法。分析:矩形类数据成员包括左上角坐标(left和top)和矩形的长(length)和宽(width),定义成员函数set()设置矩形,成员函数area()计算矩形的面积。#include class CRectpublic:CRect();void set(double t,double,double len,double wid);double area();private:double left;double top;double length;double width;CRect:CRect()top=0;/相当与this-top=0 left=0;length=0;width=0;C+程序设计类与对象第章362022-12-136void CRect:set(double t,double lef,double len,double wid)top=t;left=lef;length=len;width=wid;double CRect:area()return length*width;void main()CRect r1,r2;r1.set(10,10,10,10);r2.set(200,200,15,25);cout第一矩形的面积是:r1.area()endl;cout第二矩形的面积是:r2.area()top=t;left=lef;/等价与等价与this-left=lef;length=len;/等价与等价与this-length=len;width=wid;/等价与等价与this-width=wid;C+程序设计类与对象第章371.5 类和对象的进一步引用 在程序中经常需要访问对象中的成员,访问对象中的成员可以有3种方法:通过对象名和成员运算符访问对象中的成员;通过指向对象的指针和指针运算符访问对象中的成员;通过对象的引用变量访问对象中的成员。C+程序设计类与对象第章381.4 构造函数和析构函数构造函数和析构函数的作用1.构造(constructor)函数 在创建对象时,系统自动调用它来初始化数据成员;2.析构(destructor)函数 在对象生存期结束的时候,自动调用来释放该对象。C+程序设计类与对象第章391.4.1 构造函数的定义一、构造函数的定义格式:class 类名public:构造函数函数名(参数表);C+程序设计类与对象第章401.4.1 构造函数的定义二、构造函数的特点(1)构造函数是类的一个特殊的成员函数,函数名与类名相同;(2)构造函数的访问属性应该是公有(public)访问属性;(3)构造函数的功能是对对象进行初始化,因此在构造函数中只能对数据成员做初始化,这些数据成员一般为私有成员,在构造函数中一般不做初始化以外的事情;(4)构造函数可以在类内定义也可以在类外定义;(5)构造函数无函数返回类型。注意:是什么也不写,也不可写void;(6)在程序运行时,当新的对象被建立,该对象所属的类的构造函数自动被调用,在该对象生存期中也只调用这一次。(7)构造函数可以重载。类中可以有多个构造函数,它们由不同的参数表区分,系统在自动调用时按一般函数重载的规则选一个执行。C+程序设计类与对象第章41【例1.3】定义Cdate一个不带参数的构造函数。#include class Cdate/定义一个日期类 public:/公有数据成员 Cdate()/定义构造函数 year=2000;/初始化数据成员 month=1;day=1;void Print()/一般成员函数 coutyear-month-dayendl;private:int year,month,day;int main()Cdate d1;/自动调用构造函数,初始化数据成员 d1.Print();return 0;程序运行结果为:程序运行结果为:2000-1-1 C+程序设计类与对象第章42【例1.4】Cdate类带参数的构造函数#include class Cdatepublic:Cdate(int y,int m,int d);void Print();private:int year,month,day;Cdate:Cdate(int y,int m,int d)year=y;month=m;day=d;void Cdate:Print()coutyear-month-dayendl;int main()Cdate d1(1999,12,30),d2(2001,4,1);d1.Print();d2.Print();return 0;程序运行结果:1999-12-302001-4-1C+程序设计类与对象第章43三、使用初始化表来实现对数据成员的初始化初始化表的一般格式:类名:构造函数名(参数列表):初始化表 构造函数其他实现代码初始化表的格式:对象成员1(参数名或常量),对象成员2(参数名或常量),对象成员n(参数名或常量)例如例1.4定义的构造函数可以改用以下形式:Cdate:Cdate(int y,int m,int d):year(y),month(m),day(d)注意:这种写法方便、简洁。许多C+程序员喜欢用这种方式初始化所有的数据成员。除了构造函数可以使用初始化表外,类的其他成员函数也可以根据需要使用初始化表。C+程序设计类与对象第章441.4.2 构造函数的重载 程序中需要以不同的方式初始化类对象时,可以在一个类中定义多个构造函数。即可以重载构造函数。C+定义对象时,根据声明中的参数个数和类型选择相应的构造函数初始化对象。C+程序设计类与对象第章45【例1.5】Cdate类中的重载构造函数#include class Cdatepublic:Cdate();/不带参数的构造函数,又称默认构造函数 Cdate(int y);/带1个参数的构造函数 Cdate(int y,int m);/带2个参数的构造函数 Cdate(int y,int m,int d);/带3个参数的构造函数 void Print();private:int year,month,day;Cdate:Cdate()/构造函数的定义 year=2000;month=1;day=1;Cdate:Cdate(int y)year=y;month=1;day=1;Cdate:Cdate(int y,int m)year=y;month=m;day=1;Cdate:Cdate(int y,int m,int d)year=y;month=m;day=d;void Cdate:Print()coutyear-month-dayendl;int main()Cdate day1;Cdate day2(2004);Cdate day3(2005,2);Cdate day4(2006,12,16);day1.Print();day2.Print();day3.Print();day4.Print();return 0;程序运行结果:2000-1-12004-1-12005-2-12006-12-16C+程序设计类与对象第章461.4.2 构造函数的重载关于构造函数C+规定:(1)每个类必须有一个构造函数,如果没有就不能创建任何对象;(2)若没有定义任何一个构造函数,C+提供一个默认的构造函数,该构造函数没有参数,不做任何工作,相当一个空函数,例如:Cdate:Cdate()所以在讲构造函数以前也可以定义一个对象,就是因为系统提供的默认构造函数。(3)只要C+提供一个构造函数(不一定是没有参数的),C+不再提供默认的构造函数。也就是说为类定义了一个带参数的构造函数,还想要创建无参的对象时,则需要自己定义一个默认构造函数 C+程序设计类与对象第章471.4.3 默认参数的构造函数为什么使用默认参数的构造函数:对象常有一些初始值。【例1.6】设计CPoint类,它带有默认参数的构造函数。分析:一个点包括横坐标(x)和纵坐标(y)两个数据成员,默认初始值为(0,0)。也可以根据用户需要,初始化点为其他坐标,因此需要定义一个带默认参数的构造函数。定义成员函数print()输出该点。CPoint-x:double-y:double+CPoint(x1:double=0,y1:double=0)+print():voidCPoint类图类图 C+程序设计类与对象第章48#include class CPointpublic:CPoint(double x1=0,double y1=0)/带默认参数的构造函数 x=x1;y=y1;void print()cout(x,y)endl;private:double x,y;main()CPoint point1;/自动调用带默认参数的构造函数,(0,0)CPoint point2(10);/自动调用带默认参数的构造函数,(10,0)CPoint point3(20,30);/自动调用带默认参数的构造函数,(20,30)point1.print();point2.print();point3.print();注意:构造函数是对象创建时自动调用的,不能显式调用。程序运行结果:程序运行结果:(0,0)(10,0)(20,30)C+程序设计类与对象第章49 一个类可以包括多个构造函数,但对每一个对象来说,对象创建时只能执行其中一个与对象参数相匹配的构造函数。下面代码存在二义性问题:#include class CPointpublic:/带默认参数的构造函数带默认参数的构造函数 CPoint(double x1=0,double y1=0)x=x1;y=y1;/带一个参数的构造函数带一个参数的构造函数 CPoint(double x1)x=x1;y=100;/其他成员函数其他成员函数 private:double x,y;main()CPoint point1(10);/错误,匹配哪个构造函数不确定错误,匹配哪个构造函数不确定 point1.print();C+程序设计类与对象第章501.4.4 析构函数 一、析构函数的作用 是一个特殊类成员函数,它的作用与构造函数相反。析构函数的作用是在对象生存期结束之前自动执行,做清理工作。例如:一个类可能在构造函数中分配资源,这些资源要在对象的生存期结束以前释放,释放资源的工作就是自动调用类的析构函数完成的。二、析构函数的特点(1)析构函数函数的名字特别,是类名加“”字符,表明它与构造函数相反;(2)析构函数没有参数,不能指定返回值类型;(3)一个类中只能定义一个析构函数,所以析构函数不能重载;(4)在对象生存期结束时,系统自动调用析构函数。C+程序设计类与对象第章511.4.4 析构函数三、析构函数的定义格式类名:析构函数名()/实现代码例如:Cdate:Cdate()C+程序设计类与对象第章52【例1.7】设计一个简单的字符串类CString,类中有两个数据成员,分别表示字符串的长度和字符串的内容。定义一个构造函数和一个析构函数,成员函数GetLength()返回字符串长度,成员函数GetContents()获得字符串的内容,重载成员函数SetContents()可以改变字符串的值。字符串类类图CString-length:int-contents:char*+CString()+CString(s:char*)+CString()+GetLength():int+GetContents(str:char*):void+SetContents(len:int,cont:char*):void+SetContents(cont:char*):voidC+程序设计类与对象第章53#include#include class CString public:CString();/默认构造函数 CString(char*s);/带参数的构造函数 CString();/析构函数 int GetLength();/得到字符串的长度 void GetContents(char*str);/得到字符串的内容 void SetContents(int len,char*cont);/重新设置字符串 void SetContents(char*cont);/重载成员SetContents(),重新设置字符串private:int length;/字符串长度 char*contents;/字符串内容;CString:CString()length=0;contents=NULL;cout 字符串对象初始化:默认构造函数 endl;C+程序设计类与对象第章54CString:CString(char*s)length=strlen(s);contents=new charlength+1;strcpy(contents,s);cout 字符串对象初始化:一个参数的构造函数 endl;CString:CString()cout contents 被销毁 endl;if(contents!=NULL)delete contents;int CString:GetLength()return length;void CString:GetContents(char*str)strcpy(str,contents);C+程序设计类与对象第章55void CString:SetContents(int len,char*cont)length=len;if(contents!=NULL)delete contents;contents=new charlen+1;strncpy(contents,cont,length);contents length=0;cout 两个参数的SetContents函数 endl;void CString:SetContents(char*cont)length=strlen(cont);if(contents!=NULL)delete contents;contents=new charlength+1;strcpy(contents,cont);cout 一个参数的SetContents函数 endl;C+程序设计类与对象第章56void main()CString str1,str2;/两次调用默认的构造函数 CString str3(第三个字符串);/调用带参数的构造函数 str1.SetContents(第一个字符串);/调用一个参数的SetContents函数 str2.SetContents(20,第二个字符串两个参数);/调用两个参数的SetContents函数 int i=str1.GetLength();char string100;str1.GetContents(string);cout i string endl;i=str2.GetLength();str2.GetContents(string);cout i string endl;i=str3.GetLength();str3.GetContents(string);cout i string endl;字符串对象初始化:默认构造函数(执行str1的构造函数)字符串对象初始化:默认构造函数(执行str2的构造函数)字符串对象初始化:一个参数的构造函数(执行str3的构造函数)一个参数的SetContents函数两个参数的SetContents函数12 第一个字符串20 第二个字符串两个参数12 第三个字符串第三个字符串被销毁 (执行str3的析构函数)第二个字符串两个参数被销毁 执行str3的析构函数)第一个字符串被销毁 (执行str3的析构函数)C+程序设计类与对象第章571.4.4 析构函数 四、构造函数和析构函数的调用时间和调用顺序 构造函数在创建对象时自动调用,调用的顺序是按照对象定义的次序。析构函数的调用顺序正好与构造函数相反。也就是说对于同一存储类别的对象是先构造的对象后析构,后构造的对象先析构。C+程序设计类与对象第章581.4.5 拷贝构造函数和默认拷贝构造函数 一、拷贝构造函数的作用 用一个已知的对象来初始化另一个对象二、拷贝构造函数定义格式 类名:拷贝构造函数名(类名&引用名)例如:Tdate:Tdate(Tdate&d);/形参是一个对象的引用 CString(const CString&stringSrc);/形参是一个const的对象引用三、通常在下述三种情况下,需要用拷贝初始化构造函数:(1)明确表示由一个对象初始化另一个对象时;如Cdate day3(d1);(2)当对象作为函数实参传递给函数形参时;如fun(Cdate day);(3)当对象作为函数的返回值,创建一个临时对象时。C+程序设计类与对象第章59【例1.8】设计一个复数类,两个数据成员分别表示复数的实部(real)和虚部(imag),2个构造函数分别在不同的情况下初始化对象,函数Print()用于输出复数。#include class CComplex public:CComplex(double,double);CComplex(CComplex&c);/复数类的拷贝构造函数声明 CComplex add(CComplex&x);/复数相加,函数返回值为两个复数的和 void Print();private:double real;double imag;CComplex:CComplex(double r=0.0,double i=0.0)real=r;imag=i;cout调用两个参数的构造函数endl;C+程序设计类与对象第章60CComplex:CComplex(CComplex&c)/复数类的拷贝构造函数定义 real=c.real;imag=c.imag;cout调用拷贝构造函数endl;void CComplex:Print()/显示复数值 cout (real ,imag )endl;void f(CComplex n)/对象作为函数参数 coutn=;n.Print();CComplex CComplex:add(CComplex&x)/函数的返回值为对象 CComplex y(real+x.real,imag+x.imag);/调用两个参数的构造函数 return y;/调用复数类的拷贝构造函数 void main(void)CComplex a(3.0,4.0),b(5.6,7.9);CComplex c(a);/调用复数类的拷贝构造函数 cout a=;a.Print();cout c=;c.Print();f(b);/对象作为函数实参传递给函数形参,调用拷贝构造函数 c=a.add(b);/对象作为函数的返回值,要调用拷贝构造函数 c.Print();程序运行结果:程序运行结果:调用两个参数的构造函数调用两个参数的构造函数调用两个参数的构造函数调用两个参数的构造函数调用拷贝构造函数调用拷贝构造函数a=(3,4)c=(3,4)调用拷贝构造函数调用拷贝构造函数n=(5.6,7.9)调用两个参数的构造函数调用两个参数的构造函数调用拷贝构造函数调用拷贝构造函数(8.6,11.9)C+程序设计类与对象第章61调用拷贝构造函数各对象数据成员变化 C+程序设计类与对象第章621.4.5 拷贝构造函数和默认拷贝构造函数 当用户自定义了拷贝构造函数,所用一个对象创建另一个对象时,系统自动调用了用户自定义拷贝构造函数。如果用户没有自己定义拷贝构造函数,那么编译系统会自动会提供一个默认的拷贝构造函数。四、默认拷贝构造函数 默认的拷贝构造函数所做的工作是将一个对象的全部数据成员赋值另一个对象的数据成员。C+把这种只进行对象数据成员简单赋值,称之为“浅拷贝”。问题:什么时候一定需要用户自己定义拷贝构造函数呢?C+程序设计类与对象第章63【例1.9】设计一个班级类CClass,两个数据成员name和num分别表示班级的名称和人数。函数Print()输出班级名称和班级人数。#include#include class CClasspublic:CClass(char*cName=,int snum=0);CClass()cout析构班级:pnameendl;delete pname;void Print();private:char*pname;int num;CClass:CClass(char*cName,int snum)int length=strlen(cName);pname=new charlength+1;if(pname!=0)/或或pname!=NULL strcpy(pname,cName);num=snum;cout创建班级:创建班级:pnameendl;void CClass:Print()c o u t p n a m e 班 的 人 数 为:班 的 人 数 为:numendl;void main()CClass c1(计算机计算机061班班,56);CClass c2(c1);c1.Print();c2.Print();/运行该程序出现错误!运行该程序出现错误!“浅拷贝浅拷贝”引起的运行错误引起的运行错误C+程序设计类与对象第章64c1、c2内存分配情况(浅拷贝)C+程序设计类与对象第章65问题解决:CClass(CClass&p)pname=new charstrlen(p.pname)+1;if(pname!=0)strcpy(pname,p.pname);num=p.num;cout创建班级的拷贝:pname Print();/通过指针引用对象成员,输出日期200-1-1 (*dp2).Print();/通过指针指向的对象引用对象成员,输出日期2006-12-1 delete dp1;/调用dp1的析构函数 delete dp2;/调用dp2的析构函数 C+程序设计类与对象第章691.5.2 对象数组对象数组中的每一个元素都是类的对象一、声明一维对象数组的语法形式 类名 数组名常量表达式;注意:在建立数组时,同样要调用构造函数初始化每个数组元素。如:CStudent s3=CStudent(张三,2006001,19);/初始化第1个数组元素CStudent(李四,2006002,18);/初始化第2个数组元素CStudent(王五,2006003,20;/初始化第3个数组元素二、引用对象数组元素的公有成员:数组名下标.成员名;C+程序设计类与对象第章70【例1.10】设计一个学生类,假设一个班有5个学生,计算这个班学生的平均年龄。分析:可以定义一个学生对象数组表示5个学生对象,为了计算学生的平均年龄,学生类中需要有一个得到学生年龄的成员函数GetAge()。#include#include class CStudentpublic:CStudent(char*na,int i,int a);int GetAge();private:char name20;int id;int age;CStudent:CStudent(char*na,int i,int a)strcpy(name,na);id=i;age=a;int CStudent:GetAge()return age;void main()int sum=0;CStudent s5=CStudent(张三张三,10001,20),/对象数组初始化对象数组初始化CStudent(李四李四,10002,22),CStudent(王五王五,10003,24),CStudent(赵六赵六,10004,26),CStudent(孙六孙六,10005,28);for(int i=0;i5;i+)sum+=si.GetAge();/累加每个学生的年龄累加每个学生的年龄cout sum/5 endl;运行结果为运行结果为 24C+程序设计类与对象第章711.5.3 类对象作为成员有类对象作为成员称为组合类一、通过构造函数的初始化表为内嵌对象初始化 格式为:类名:构造函数(参数表):内嵌对象1(参数表1),内嵌对象2(参数表2),构造函数体二、组合类构造函数的执行顺序为:(1)按内嵌对象的声明顺序依次调用内嵌对象的构造函数(2)然后执行组合类本身的构造函数。C+程序设计类与对象第章72【例1.11】设计一个学生类。数据成员包括学生的姓名、学号、性别、出生日期。分析:学生的姓名、性别可以用简单的数据类型表示,学生的学号和出生日期可以用类对象表示。定义三个类:学生类、日期类和学号类。学生类是一个组合类,类的数据成员包括其他类对象,因此学生类的构造函数中使用初始化表初始化类对象数据成员。Cdate-year:int-month:int-day:int+Cdate(int,int,int)+Cdate(s:Cdate&)+Cdate()CStudentID-value:int+CStudentID(i:int)+CStudentID()CStudent-name20:char-sex:char-id:CStudentID-birthday:Cdate+CStudent(char*,char,int,Cdate&)+CStudent()C+程序设计类与对象第章73#include#include/日期类声明class Cdate public:Cdate(int y=1985,int m=1,int d=1)/日期类的构造函数 year=y;month=m;day=d;cout调用日期类的构造函数endl;Cdate(Cdate&s)/日期类的拷贝构造函数 year=s.year;month=s.month;day=s.day;cout调用日期类的拷贝构造函数endl;Cdate()cout调用日期类的析构函数endl;private:int year,month,day;/定义3个整型变量,分别表示日期的年月日;C+程序设计类与对象第章74/学号类声明class CStudentIDpublic:CStudentID(int i)value=i;cout构造学号valueendl;CStudentID()cout析构学号valueendl;private:int value;C+程序设计类与对象第章75/学生类声明class CStudent public:CStudent(char*,char,int,Cdate&);CStudent();private:char name20;char sex;CStudentID id;Cdate birthday;CStudent:CStudent(char*na,char s,int i,Cdate&d):id(i),birthday(d)strcpy(name,na);sex=s;cout调用学生 name 的构造函数endl;CStudent:CStudent()cout调用学生调用学生 name的析构函数的析构函数endl;int main()Cdate day1(2000,1,1);/定义学生对象定义学生对象 CStudent stud1(张三张三,m,2006102,day1);return 0;程序运行结果为:程序运行结果为:调用日期类的构造函数调用日期类的构造函数构造学号构造学号调用日期类的拷贝构造函数调用日期类的拷贝构造函数调用学生张三的构造函数调用学生张三的构造函数调用学生张三的析构函数调用学生张三的析构函数调用日期类的析构函数调用日期类的析构函数析构学号析构学号调用日期类的析构函数调用日期类的析构函数C+程序设计类与对象第章76 例1.11类调用过程 54321Cdate day1(2000,1,1);Cdate(int y=1985,int m=1,int d=1)d=1);CStudent stud1(张三张三,m,2006102,day1);CStudent(char*na,char s,int i,Cdate&d):id(i),birthday(d)CStudentID(int i);Cdate(Cdate&s);C+程序设计类与对象第章771.5.4 面向对象程序中
展开阅读全文