C++教程第2章.ppt_第1页
C++教程第2章.ppt_第2页
C++教程第2章.ppt_第3页
C++教程第2章.ppt_第4页
C++教程第2章.ppt_第5页
已阅读5页,还剩101页未读 继续免费阅读

下载本文档

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

文档简介

第二章类与对象 主讲教师 张亚玲教授 学习目标 掌握类的概念理解对象与类的关系 掌握对象的创建和使用掌握构造函数 析构函数的概念及使用方法掌握内存的动态分配的概念和使用方法掌握对象数组和对象指针掌握函数调用中参数的传递掌握友元函数和友元类的定义及使用 类的定义格式 class类名 private 数据成员或成员函数protected 数据成员或成员函数public 数据成员或成员函数 声明一个图书类 分析 图书都有书名 作者 出版社和价格 对于图书的基本操作有输入 输出图书信息 因此首先抽象出所有图书都具有的属性 书名 作者 出版社和价格 然后用成员函数实现对图书信息的输入和输出 classBook private chartitle 20 author 10 publish 30 floatprice public voidInput voidPrint 说明 类声明中的private protected和public关键字可以按任意顺序出现 为了使程序更加清晰 应将私有成员 保护成员和公有成员归类存放 默认时的访问权限为私有的 private 对于一个具体的类 类声明中的private protected和public三个部分不一定都要有 但至少应该有其中一个部分 数据成员可以是任何数据类型 但不能用自动 auto 寄存器 register 或外部 extern 类型进行说明 由于类是一种数据类型 系统并不会为其分配内存空间 所以不能在类声明中给数据成员赋初值 错误的类声明 声明一个长方形类 分析 长方形有长和宽 对于长方形可以计算其面积和周长 因此抽象出所有长方形都具有的属性长和宽 然后用成员函数实现求面积和求周长运算 classRectangle private doublelength 3 5 doublewidth 4 6 public doubleArea doublePerimeter 说明 1 一般将类的声明放在头文件 h中 而将成员函数的实现放在 cpp文件中 2 类定义必须以 结束 成员函数的定义 返回类型类名 函数名 参数表 函数体 voidBook Input cin title author publish price voidBook Print cout title author publish price endl 说明 如果在类外定义成员函数 则应在所定义的成员函数名前缀上类名 在类名和函数名之间应加上作用域运算符 它说明成员函数从属于哪个类 例如上例中的 voidBook Input 表示成员函数Input是Book类中的函数 在定义成员函数时 对函数所带的参数 不但要说明它的类型 还要指出其参数名 在定义成员函数时 其返回类型一定要与函数原型声明的返回类型匹配 对象的定义与使用 对象的定义先声明类 然后在使用的时候再定义对象 定义格式与一般变量定义格式相同例如 Bookb1 b2 此时定义了b1和b2为Book类的两个对象 类名对象名列表 说明 必须定义了类以后 才能定义类的对象 多个对象之间用逗号分隔 声明了一个类就声明了一种类型 它并不能接收和存储具体的值 只能作为生成具体对象的一种 样板 只有定义了对象后 系统才为对象分配存储空间 对象的使用 Bookb1 b1 Input 通过对象b1执行输入操作b1 Print 通过对象b1执行输出操作 例2 3 图书类的完整程序 对象名 成员函数名 实参表 classBook 该段程序放在名为book h的文件中 private chartitle 20 author 10 publish 30 floatprice public voidInput voidPrint include iostream 该段程序放在名为book cpp的文件中usingnamespacestd include book h intmain Bookb1 b1 Input cout 运行结果 endl b1 Print return0 voidBook Input cout title author publish price voidBook Print cout title author publish price endl defineN5 定义常量N表示有N个学生 student hstructStudent 声明结构体类型 用来表示学生 longlNum 学号charcName 12 姓名floatfGrade 成绩 classOurClass 声明一个班级类 private Students N 定义N个学生public voidInput 输入学生信息voidPrint 输出学生信息voidSort 按学生成绩进行排序 思考 OurClass类定义的不妥之处 1 类名定义不妥 应该为StudentClass 2 应该补充属性 classNamenumberleaderName 例2 4 学生成绩管理 例1 1 的完整程序 include iostream student cppusingnamespacestd include student h intmain OurClasscl 定义对象stucl Input 输入N个学生的信息 学号 姓名 成绩cl Sort 按学生成绩进行排序cout s i lNum s i cName s i fGrade voidOurClass Sort inti j StudentTemp for i 0 i N 1 i for j i 1 j N j if s i fGrade s j fGrade 交换学生信息Temp s i s i s j s j Temp voidOurClass Print inti cout 学号姓名成绩 endl for i 0 i N i cout s i lNum s i cName s i fGrade endl 对象的赋值 同类型的变量 如整型 实型 结构体类型等的变量可以利用赋值操作符 进行赋值 对于同类型的对象也同样适用 也就是说 同类型的对象之间可以进行赋值 这种赋值默认通过成员复制进行 当对象进行赋值时 对象的每一个成员逐一复制 赋值 给另一个对象的同一成员 例2 5 对象的赋值 平面上的点的赋值 注意 在使用对象赋值语句进行赋值时 两个对象的类型必须相同 赋值兼容规则除外 两个对象间的赋值 仅仅使这些对象中的数据相同 而两个对象仍然是彼此独立的 各自有自己的内存空间 如果类中存在指针 则不能简单的将一个对象的值赋给另一个对象 否则可能会产生错误 classPoint 该段程序放在名为point h的文件中private intx y public voidSetPoint inta intb voidPrint include iomanip 该段程序放在名为point cpp的文件中 include iostream usingnamespacestd include point h intmain Pointp1 p2 定义对象p1 p2p1 SetPoint 3 5 cout p1 endl p1 Print cout p2 endl p2 Print p2 p1 对象赋值cout p2 p1 endl p2 Print return0 voidPoint SetPoint inta intb x a y b voidPoint Print cout x x setw 5 y y endl 构造函数和析构函数 如果变量在使用之前没有正确初始化或清除 将导致程序出错 因此要求对对象必须正确地进行初始化 构造函数的功能是在创建对象时 给数据成员赋初值 即对象的初始化 思考 如何给一个对象的私有属性初始化值 2种方法 构造函数 定义构造函数原型的格式为 在类外定义构造函数的定义格式为 类名 形参列表 类名 类名 形参列表 函数语句 说明 构造函数的名字必须与类名相同 构造函数可以有任意类型的参数 但是没有返回值类型 也不能指定为void类型 定义对象时 编译系统会自动地调用构造函数 通常构造函数被定义在公有部分 如果没有定义构造函数 系统会自动生成一个缺省的构造函数 只负责对象的创建 不做任何初始化工作 构造函数可以重载 例2 6 构造函数应用举例 输出日期 classDate 该段程序放在名为date h的文件中private intyear month day public Date inty intm intd voidPrint include iostream 该段程序放在名为date cpp的文件中usingnamespacestd include date h intmain Datetoday 2000 3 1 cout todayis today Print return0 Date Date inty intm intd year y month m day d voidDate Print cout year month day endl 注意 在实际应用中 通常需要给每个类定义构造函数 如果没有给类定义构造函数 则系统自动生成一个默认的构造函数 类名 构造函数名 构造函数可以不带参数 Point Point x 0 y 0 classPoint private intx y public Point 注意 构造函数也可采用构造初始化表对数据成员进行初始化 classDate private intyear month day public Date inty intm intd year y month m day d 构造函数初始化表对数据成员进行初始化 注意 如果数据成员是数组 则应在构造函数中使用相关语句进行初始化 classStudent private charname 10 intage public Student charna inta Student Student charna inta age a strcpy name na name是字符数组 所以用strcpy函数进行初始化 析构函数 析构函数的功能是在对象删除之前 用它来做一些内存释放等清理工作 它的功能与构造函数的功能正好相反 析构函数 定义析构函数的一般格式析构函数的特点析构函数名是由 和类名组成的 析构函数没有参数 也没有返回值 而且也不能重载 通常析构函数被定义在公有部分 并由系统自动调用 一个类中有且仅有一个析构函数 且应为public 类名 析构函数 例2 7 构造函数与析构函数的执行顺序 Point类的多个对象的创建与释放 例2 8 对象定义在函数体内 析构函数的执行情况 例2 9 复合语句中对象的析构函数的执行情况 例2 7 classPoint 该段程序放在名为point h的文件中private intx y public Point inta intb Point include iostream 该段程序放在名为point cpp的文件中usingnamespacestd include point h intmain Pointp1 1 2 p2 3 5 return0 Point Point inta intb 定义构造函数 cout constructor endl x a y b cout x y endl Point Point 定义析构函数 cout destructor endl cout x y endl 例2 8 classComplex complex hprivate doublereal imag public Complex doubler doublei Complex include iostream usingnamespacestd include complex h voidfun Complexc intmain cout insidemain endl Complexc1 1 1 2 2 fun c1 cout outsidemain endl return0 Complex Complex doubler doublei cout constructor endl real r imag i Complex Complex cout destructor endl voidfun Complexc cout insidefun endl 思考 是不是每个类都需要有析构函数 具有什么特点的类必须提供析构函数 内存的动态分配 用户存储区空间分三个部分 程序区 代码区 静态存储区 数据区 和动态存储区 栈区和堆区 代码区存放程序代码 程序运行前就分配存储空间 数据区存放常量 静态变量 全局变量等 栈区存放局部变量 函数参数 函数返回值和临时变量等 堆区是程序空间中存在的一些空闲存储单元 这些空闲存储单元组成堆 在堆中创建的数据对象称为堆对象 当创建对象时 堆中的一些存储单元从未分配状态变为已分配状态 当删除所创建的堆对象时 这些存储单元从已分配状态又变为未分配状态 当堆对象不再使用时 应予以删除 回收其所占用的动态内存 运算符new 在C 程序中 运算符new的功能是实现内存的动态分配 new运算符的使用格式包括三种形式 说明T是一个数据类型名 表示在堆中建立一个T类型的数据 初始值列表可以省略 例如 int p float p1 p newint 100 让p指向一个类型为整型的堆地址 该地址中存放值100p1 newfloat 让p1指向一个类型为实型的堆地址 指针变量 newT 指针变量 newT 初始值列表 指针变量 newT 元素个数 运算符new 用new创建堆对象的格式 类名 指针名 new类名 构造函数参数 Complex c1 newComplex 1 1 2 2 创建对象 c1 并调用构造函数初始化数据成员real imag为1 1 2 2new后面的类的类型后面是否跟参数取决于构造函数 classDate private intyear month day public Date intmain Date today newDate Date Date year 2001 month 12 day 3 运算符new new返回一个指定的合法数据类型的内存空间的首地址 指针 若分配不成功 则返回一个空指针 new可以为数组动态分配内存空间 这时应该在类型名后面指明数组大小 其中 元素个数是一个整型数值 可以是常数也可以是变量 指针类型应与数组类型一致 例如 intn p cin n p newint n 表示new为具有n个元素的整型数组分配了内存空间 并将首地址赋给了指针p 运算符new new不能对动态分配的数组存储区进行初始化 例如 int p p newint 10 0 错误 不能对动态分配的数组进行初始化用new分配的空间 使用结束后只能用delete显式地释放 否则这部分空间将不能回收而造成内存泄露 运算符delete 运算符delete用来释放动态变量或动态数组所占的内存空间 delete运算符的应用格式如下释放动态变量所占的内存空间例如 int p newint deletep 释放指针p所指向的动态内存空间 delete指针变量名 delete 指针变量名 运算符delete 释放动态数组所占的内存空间例如 int p p newint 10 delete p 释放为数组动态分配的内存说明new和delete需要配套使用 如果搭配错了 程序运行时将会发生不可预知的错误 在用delete释放指针所指的空间时 必须保证这个指针所指的空间是用new申请的 并且只能释放一次 否则将产生指针悬挂问题 见第七章运算符重载 运算符delete 如果在程序中用new申请了空间 就应该在结束程序前释放所有申请的空间 这样才能保证堆内存的有效利用 当delete用于释放由new创建的数组的连续内存空间时 无论是一维数组还是多维数组 指针变量名前必须使用 且 内没有数字 例2 10 动态创建类Point的对象 classPoint point h private intx y public Point inta intb Point include iostream point cppusingnamespacestd include point h intmain Point p newPoint 1 3 动态创建对象 自动调用构造函数deletep 删除对象 自动调用析构函数return0 Point Point inta intb cout constructor endl x a y b Point Point cout destructor endl 一个重要问题 如果一个类中定义了动态申请的数据成员 则不得使用对象赋值进行该类两个对象之间的赋值 思考 如果违反产生什么后果 对象数组和对象指针 对象数组数组的元素可以是基本数据类型的数据 也可以是用户自定义数据类型的数据 对象数组是指每一个数组元素都是对象的数组 对象数组的元素是对象 它不仅具有数据成员 而且还有成员函数 声明对象数组的形式 类名数组名 下标表达式 数组名 下标 成员函数 对象数组的引用形式 对象数组 实际上 不仅可以说明基本数据类型或符合数据类型的数组 在程序中定义了一个类之后 还可以说明该类对象的数组 因为类实质上是一个数据类型 因此说明对象数组的方法与说明普通数组的方法非常相似 惟一需要进一步讲述的是初始化对象数组的方法 1 使用缺省构造函数完成初始化方法2 使用带参数的构造函数初始化数组 参见P106 对象数组 定义 类名数组名 元素个数 访问方法 通过下标访问数组名 下标 成员名 数组 对象数组初始化 数组中每一个元素对象被创建时 系统都会调用构造函数类初始化该对象 通过初始化列表赋值 例 LocationA 2 Location 1 2 Location 3 4 如果没有为数组元素指定显式初始值 数组元素使用缺省值初始化 调用缺省构造函数 数组 数组元素所属类的构造函数 不定义构造函数 则采用缺省构造函数 各元素对象的初值要求为相同的值时 可以定义出具有缺省形参值的构造函数 各元素对象的初值要求为不同的值时 需要定义带形参 无缺省值 的构造函数 当数组中每一个对象被删除时 系统都要调用一次析构函数 数组 对象数组应用举例 Location h if defined LOCATION H define LOCATION HclassLocation public Location Location intxx intyy Location voidMove intx inty intGetX returnX intGetY returnY private intX Y endif include include Location h Location Location X Y 0 cout DefaultConstructorcalled endl Location Location intxx intyy X xx Y yy cout Constructorcalled endl Locatuon Location cout Destructorcalled endl voidLocation Move intx inty X x Y y intmain cout Enteringmain endl LocationA 2 for inti 0 i 2 i A i Move i 10 i 20 cout Exitingmain endl return0 运行结果 Enteringmain DefaultConstructorcalled DefaultConstructorcalled Exitingmain Destructorcalled Destructorcalled 对象数组和对象指针 例2 11 对象数组的应用 求圆的面积 例2 12 输出若干个平面上的点访问一个对象既可以通过对象名访问 也可以通过对象地址访问 对象指针就是用于存放对象地址的变量 对象指针遵循一般变量指针的各种规则 声明对象指针的一般语法形式为 类名 对象指针名 对象指针名 成员名 例2 11 classCircle 该段程序放在名为circle h的文件中 private doubleradius public Circle doubler doubleArea Circle include iostream 该段程序放在名为circle cpp的文件中usingnamespacestd include circle h intmain Circlec 3 1 3 5 inti for i 0 i 3 i cout c i Area endl return0 Circle Circle doubler cout construct endl radius r doubleCircle Area return3 14 radius radius Circle Circle cout destruct endl 例 2 12 输出若干个平面上的点 classPoint point hprivate intx y public Point inta intb voidPrint include iostream point cppusingnamespacestd include point h intmain Pointob 3 Point 1 2 Point 3 4 Point 5 6 inti for i 0 i 3 i ob i Print return0 Point Point inta intb x a y b voidPoint Print cout x y endl 在定义对象数组ob时系统会自动调用构造函数进行初始化 然而此时构造函数的参数是2个 因此就需要通过直接调用构造函数给对象数组赋值 即 Pointob 3 Point 1 2 Point 3 4 Point 5 6 从而实现对象数组的初始化操作 classPoint point hprivate intx y public Point inta intb voidPrint include iostream point cppusingnamespacestd include point h intmain Pointob 3 Point 1 2 Point 3 4 Point 5 6 inti for i 0 i 3 i ob i Print return0 Point Point inta intb x a y b voidPoint Print cout x y endl 对象指针 例如 Circle c1 c 3 c1 c3 Area 错误 不能使用没有初始化的对象指针 对象指针 例2 13 用对象指针访问Circle类的成员函数 例2 14 用对象指针引用Circle类的对象数组 例2 13 intmain Circle c newCircle 3 coutArea endl deletec return0 例2 14 intmain Circlec 3 1 3 5 Circle p c for pArea endl return0 自引用指针this 当定义了一个类的若干对象后 每个对象都有属于自己的数据成员 而同一类的不同对象将共同拥有一份成员函数的拷贝 那么在执行不同对象所对应的成员函数时 各成员函数是如何分辨出当前调用自己的是哪个对象呢 使用this指针时应该注意以下几点 this指针是一个const指针 不能在程序中修改它或给它赋值 this指针是一个局部数据 它的作用域仅在一个对象的内部 静态成员函数不属于任何一个对象 在静态成员函数中没有this指针 例2 16 this应用举例 通过成员函数copy实现Square类对象的赋值 C 为非静态成员函数提供了一个名字为this的指针 这个指针称为自引用指针 每当对象调用成员函数时 系统就将该对象的地址赋给this指针 这时C 编译器将根据this指针所指向的对象来确定应该引用哪一个对象的数据成员 例2 15 输出不同正方形的面积 classSquare square h private doublelength public Square doublel doubleArea include iostream square cppusingnamespacestd include square h intmain Squares1 3 s2 5 coutlength l doubleSquare Area returnlength length 例2 16 this应用举例 通过成员函数copy实现Square类对象的赋值 classSquare square h private doublelength public Square doublel doubleArea voidcopy Squares include iostream square cppusingnamespacestd include square h intmain Squares1 3 s2 5 cout beforecopy endl cout s1areais s1 Area endl cout aftercopy endl s1 copy s2 cout s1areais s1 Area endl return0 Square Square doublel length l doubleSquare Area returnlength length voidSquare copy Squares if this 函数参数的传递机制 使用对象作为函数参数当进行函数调用时 需要给形参分配存储单元 形参和实参结合是值传递 实参将自己的值传递给形参 形参实际上是实参的副本 这是一种单向传递 形参的变化不会影响到实参 例2 17 对象作为函数参数 求平面上的点向东 向北移动1格的新坐标使用对象指针作为函数参数当进行函数调用时 需要给形参分配存储单元 形参和实参的结合是地址传递 实参将自己的地址传递给形参 形参的变化会直接影响到实参 函数参数的传递机制 使用对象引用作为函数参数当进行函数调用时 在内存中并没有产生实参的副本 它是直接对实参操作 这种方式是双向传递 形参的变化会直接影响到实参 与指针作函数参数比较 这种方式更容易使用 更清晰 而且当参数传递的数据较大时 用引用比用一般变量传递参数的效率和所占空间都好 引用 即一个变量的别名 如果用于形参 则和实参直接公用内存单元 例3 6输入两整数 交换后输出 includevoidSwap inta intb intmain intx 5 y 10 cout x x y y endl Swap x y cout x x y y endl return0 函数的定义与使用 voidSwap inta intb intt t a a b b t 运行结果 x 5y 10 x 5y 10 例输入两整数 交换后输出 includevoidSwap int voidSwap int 运行结果 x 5y 10 x 10y 5 例引用调用举例 include includevoidfiddle intin1 int 函数的定义与使用 voidfiddle intin1 int 运行结果 Thevaluesare712Thevaluesare107112Thevaluesare7112 例2 19 友元 类的主要特点之一是数据隐藏 也就是类的私有成员只能通过它的成员函数来访问 有没有办法允许在类外对某个对象的私有成员进行操作呢 在C 中提供了友元机制来解决上述问题 友元既可以是不属于任何类的一般函数 也可以是另一个类的成员函数 还可以是整个的一个类 这时 这个类中的所有成员函数都可以成为友元函数 友元 在进行面向对象程序设计时 充分 合理的使用程序语言提供的封装机制 能大大提高程序的质量 特别是能够提高软件的可维护性 但是 在某些情况下 封装也会带来一些负面效应 如果为了访问对象的私有数据成员而在程序中多次调用公有的成员函数 则会带来较大开销 从而降低程序的运行效率 参见P110 友元机制是对封装机制的补充 利用这种机制 一个类可以赋予某些函数访问它的私有成员的特权 能够访问一个类的私有部分而又不是该类成员函数的函数 称为该类的友元函数 友元 为了使用友元函数 首先应该在类的定义中用关键字friend声明该类的友元函数 友元函数除了具有访问指定类的私有成员的特权之外 其他方面与普通函数完全一样 应该慎用友元 声明友元相当于在实现封装的黑盒子上开洞 如果一个类声明了很多友元 则相当于在黑盒子上开了很多个洞 显然 这将严重破坏封装性 参见P110 友元 友元是C 提供的一种破坏数据封装和数据隐藏的机制 通过将一个模块声明为另一个模块的友元 一个模块能够引用到另一个模块中本是被隐藏的信息 可以使用友元函数和友元类 为了确保数据的完整性 及数据封装与隐藏的原则 建议尽量不使用或少使用友元 友元函数 友元函数是在类定义中由关键字friend修饰说明的非成员函数 在它的函数体中能够通过对象名访问private和protected成员作用 增加灵活性 使程序员可以在封装和快速性方面做合理选择 访问对象中的成员必须通过对象名 友元 友元函数举例 include includeclassPoint public Point doublexi doubleyi X xi Y yi doubleGetX returnX doubleGetY returnY frienddoubleDistance Point 友元 doubleDistance Point 友元类 若一个类为另一个类的友元 则此类的所有成员都能访问对方类的私有成员 定义语法 将友元类名在另一个类中使用friend修饰说明 友元 友元类举例 classA friendclassB public voidDisplay cout x endl private intx classB public voidSet inti voidDisplay private Aa 友元 voidB Set inti a x i voidB Display a Display 友元函数 友元函数不是当前类中的成员函数 它可以是一个普通函数 也可以是另外一个类的成员函数 当函数被声明为一个类的友元函数后 它就可以通过对象名访问类的私有成员和保护成员 普通函数作为友元函数普通函数作为类的友元函数后 就可以通过对象访问封装在类内部的数据 可以将友元函数定义在类的内部 也可以定义在类外部 但通常都定义在类外部 friend函数返回值函数名 形参表 函数体 友元函数 例2 20 使用友元函数计算两个复数的和注意友元函数不是类的成员函数 在类外定义时 不能在函数名前加 类名 也不能通过对象来引用友元函数 友元函数在使用

温馨提示

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

最新文档

评论

0/150

提交评论