授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第5章 类和对象(一) 类的定义 对象的定义 对象的初始化 成员函数的特性 静态成员 友元 类的作用域 局部类和嵌套类 对象的生存期 第5章 类和对象(一) 类的定义 对象的定义 对象的初始化 成员函数的特性 静态成员 友元 类的作用域 局部类和嵌套类 对象的生存期 东华大学信息科学与技术学院 龚涛
第5章 类和对象(一) 5.1 类的定义 类是面向对象程序设计的核心,它实际上是一种新的数据类型,也是实现抽象类型的工具,因为类是通过抽象数据类型的方法来实现的一种数据类型。类是对某一类对象的抽象,而对象是某一种类的实例;因此,类和对象是密切相关的。 5.1.1 什么是类 类是一种复杂的数据类型,它是将不同类型的数据和与这些数据相关的操作封装在一起的集合体。因此,类具有更高的抽象性,类中的数据具有隐藏性,类还有封装性。 类的结构是用来确定一类对象的行为的,而这些行为是通过类的内部数据结构和相关的操作来确定。这些行为是通过一种操作接口来描述的,使用者只关心接口的功能,对它是如何实现的并不感兴趣。 函数只是数据和语句的封装,用于完成某种功能;类是数据和函数的封装,用于实现对某个问题的处理。 东华大学信息科学与技术学院 龚涛
5.1 类的定义 5.1.2 类的定义格式 类的定义格式一般分为说明部分和实现部分。说明部分用来说明该类中的成员,包括对数据成员的说明和成员函数的说明。成员函数用来对数据成员进行的操作,又称为“方法”。实现部分用来对成员函数进行定义。使用者关心的往往是说明部分,而实现部分是一些不必关心的信息。 类的一般定义格式如下: class <类名> { public: <成员函数或数据成员的说明> private: <数据成员或成员函数的说明> }; <各个成员函数的实现> 其中,<类名>是一种标识符,通常用T字母(Visual C++中用C字母)开始的字符串作为类名,T用来表示类,以示与对象、函数名区别。一对花括号内是类的说明部分,说明该类的成员。 东华大学信息科学与技术学院 龚涛
5.1 类的定义 5.1.2 类的定义格式 <类名>::<函数名>(<参数表>) 5.1 类的定义 5.1.2 类的定义格式 类的实现部分中,作用域运算符“::”用来标识某个成员函数是属于哪个类的,该运算符的格式如下: <类名>::<函数名>(<参数表>) 例如,日期类的实现部分如下: void TDate::SetDate(int y, int m, int d) { year=y; month=m; day=d; } int TDate::IsLeapYear() { return (year%4==0&&year%100!=0)||(year%400==0); void TDate::Print() cout<<year<<"."<<month<<"."<<day<<endl; 东华大学信息科学与技术学院 龚涛
5.1 类的定义 5.1.3 说明事项 在定义C++类时,需要注意以下几个问题: 在类体中不允许对所定义的数据成员进行初始化。 5.1 类的定义 5.1.3 说明事项 在定义C++类时,需要注意以下几个问题: 在类体中不允许对所定义的数据成员进行初始化。 类中的数据成员的类型可以是任意的,包含整型、浮点型、字符型、数组、指针和引用等,也可以是对象。另一个类的对象可以作为该类的成员,但是自身类的对象是不可以的,而自身类的指针或引用是可以的。当一个类的对象作为这个类的成员时,如果另一个类的定义在后,则需要提前说明,这种说明称为引用性说明。 一般地,在类体内先说明公有成员,它们是用户所关心的;后说明私有成员,它们是用户不感兴趣的。在说明数据成员时,一般按数据成员的类型大小,由小至大说明,这样可提高时空利用率。 通常习惯地将类定义的说明部分或整个定义部分(包含实现部分)放到一个头文件中。 东华大学信息科学与技术学院 龚涛
对象在确定了它的类以后,其定义格式如下: <类名> <对象名表> 第5章 类和对象(一) 5.2 对象的定义 对象是类的实例,对象是属于某个已知的类。因此,在定义对象之前,一定要先定义好该对象的类。 5.2.1 对象的定义格式 对象在确定了它的类以后,其定义格式如下: <类名> <对象名表> 其中,<类名>是定义的对象所属的类的名字,即所定义的对象是该类类型的对象。<对象名表>中,可以是一般的对象名,还可以是指向对象的指针名或引用名,也可以是对象数组名。 东华大学信息科学与技术学院 龚涛
5.2 对象的定义 5.2.2 对象成员的表示方法 <对象名>.<成员名> 或者 5.2 对象的定义 5.2.2 对象成员的表示方法 一个对象的成员就是该对象的类所定义的成员。对象成员有数据成员和成员函数。一般对象的成员表示如下: <对象名>.<成员名> 或者 <对象名>.<成员函数名>(<参数表>) 前者用来表示数据成员,后者用来表示成员函数。这里的“.”是一个运算符,该运算符的功能是表示对象的成员。 指向对象的指针的成员表示如下: <对象指针名>-><成员名> <对象指针名>-><成员函数名>(<参数表>) 这里的“->”是一个表示成员的运算符,它与 “.”运算符的区别是“->”用来表示对象的指针的成员,而“.”用来表示一般对象的成员。 东华大学信息科学与技术学院 龚涛
5.2.2 对象成员的表示方法 例5.1 分析该程序的输出结果。 #include <iostream.h> 5.2.2 对象成员的表示方法 例5.1 分析该程序的输出结果。 class CDate { public: void SetDate(int y, int m, int d); int IsLeapYear(); void Print(); private: int year, month, day; }; 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() cout<<year<<"."<<month<<"."<<day<<endl; #include <iostream.h> #include "cdate.h" void main() { CDate date1, date2; date1.SetDate(1996,5,4); date2.SetDate(1998,4,9); int leap=date1.IsLeapYear(); cout<<leap<<endl; date1.Print(); date2.Print(); } 东华大学信息科学与技术学院 龚涛
5.2.2 对象成员的表示方法 例5.2 分析该程序的输出结果。 #include <iostream.h> 5.2.2 对象成员的表示方法 例5.2 分析该程序的输出结果。 class CPoint { public: void SetPoint(int x, int y); int Xcoord() {return X;} int Ycoord() {return Y;} void Move(int xOffset, int yOffset); private: int X, Y; }; void CPoint::SetPoint(int x, int y) X=x; Y=y; } void CPoint::Move(int xOffset, int yOffset) X+=xOffset; Y+=yOffset; #include <iostream.h> #include "cpoint.h" void main() { CPoint p1, p2; p1.SetPoint(3,5); p2.SetPoint(8,10); p1.Move(2,1); p2.Move(1,-2); cout<<"x1="<<p1.Xcoord()<<",y1="<<p1.Ycoord()<<endl; cout<<"x2="<<p2.Xcoord()<<",y2="<<p2.Ycoord()<<endl; } 东华大学信息科学与技术学院 龚涛
第5章 类和对象(一) 5.3 对象的初始化 5.3.1 构造函数和析构函数 第5章 类和对象(一) 5.3 对象的初始化 在C++语言中,对对象进行初始化通常不使用成员初始化表,而使用构造函数。 5.3.1 构造函数和析构函数 构造函数和析构函数是在类体中说明的两种特殊的成员函数。构造函数的功能是在创建对象时,用给定的对象对对象进行初始化。析构函数的功能是用来释放一个对象,它与构造函数的功能正好相反。 构造函数的特点如下: 构造函数是成员函数,函数体可写在类体内,也可写在类体外。 构造函数是一个特殊的成员函数,该函数的名字与类名相同,该函数不指定类型说明,它有隐含的返回值,该值由系统内部使用。该函数可以没有参数,也可有参数。 构造函数可以重载,即可定义多个参数个数不同的函数。 程序中一般不直接调用构造函数,在创建对象时系统自动调用构造函数。 东华大学信息科学与技术学院 龚涛
5.3 对象的初始化 5.3.2 默认构造函数和默认析构函数 没有参数的构造函数称为默认构造函数。默认构造函数有两种:一种是系统自动提供的,另一种是程序员定义的。 在程序中,程序员可以根据需要定义默认构造函数。在一个类中,如果没有定义任何构造函数,则系统自动生成一个默认构造函数。 使用系统提供的默认构造函数给创建的对象初始化时,外部类对象和静态类对象的所有数据成员为默认值,自动类对象的所有数据成员为无意义值。 如果一个类中没有定义析构函数,则系统提供一个默认析构函数,用来释放对象。系统提供的默认析构函数格式如下: <类名>::<默认析构函数> {} 默认析构函数是一个空函数。程序员定义的析构函数可以不是一个空函数,要根据需要给出合适的函数体。 东华大学信息科学与技术学院 龚涛
5.3 对象的初始化 5.3.3 复制构造函数 复制构造函数是用一个已知对象来创造一个新对象,而新创建的对象与已知对象的数据成员的值可以相同,也可以不同。 复制构造函数除了具有前述的带参数构造函数相同的特性外,它只有一个参数,并且是对象引用。复制构造函数的格式如下: <类名>::<复制构造函数名>(<类名> & < 引用名>) { <函数体> } 如果一个类中没有定义复制构造函数,则系统会自动提供一个默认复制构造函数,作为该类的公有成员,用来根据已知对象创建与其相同的对象。 东华大学信息科学与技术学院 龚涛
第5章 类和对象(一) 5.4 成员函数的特性 5.4.1 内联函数和外联函数 第5章 类和对象(一) 5.4 成员函数的特性 在类的定义中规定在类体中说明的函数作为类的成员,称为成员函数。 5.4.1 内联函数和外联函数 类的成员函数可以分为内联函数和外联函数。内联函数是指那些定义在类体内的成员函数,即该函数的函数体放在类体内。说明在类体内、定义在类体外的成员函数叫外联函数。外联函数的函数体位于类的实现部分。 内联函数在调用时不是像一般函数那样要转去执行被调用函数的函数体,执行完成后再转回调用函数中,执行其后的语句;而是在调用函数处用内联函数体的代码来替换,这样将会节省调用开销,提高运行速度。 内联函数与带参数的宏定义比较,其代码效率是一样的,但是内联函数要优于宏定义。外联函数变成内联函数的方法只需要在函数头的前面加上关键字inline就可以了。 东华大学信息科学与技术学院 龚涛
第5章 类和对象(一) 5.4 成员函数的特性 5.4.2 重载性 5.4.3 设置参数的默认值 第5章 类和对象(一) 5.4 成员函数的特性 5.4.2 重载性 一般的成员函数都可以进行重载,构造函数可以重载,但是析构函数不能重载。 5.4.3 设置参数的默认值 成员函数可以被设置参数的默认值。一般的成员函数和构造函数都可以被设置参数的默认值。 东华大学信息科学与技术学院 龚涛
第5章 类和对象(一) 5.5 静态成员 静态成员的提出是为了解决数据共享的问题。实现共享有许多方法,例如设置全局性的变量或对象,但是全局变量或对象是有局限性的。因为全局量在程序的任何地方都可以更新,对它的可见范围没法控制。为了安全起见,在程序中很少使用全局量。 实现多个对象之间的数据共享,可以不使用全局对象,而使用静态的数据成员。静态成员包含静态数据成员和静态成员函数。它们都属于类,也属于类的所有对象。使用静态成员时可以用对象引用,也可以用类来引用。静态数据成员和静态成员函数在没有对象时就已存在,并可以用类来引用。 东华大学信息科学与技术学院 龚涛
5.5 静态成员 5.5.1 静态数据成员 静态数据成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态数据成员是类的所有对象共享的成员,而不是某个对象的成员。 使用静态数据成员可以节省内存,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样的,但它的值是可以更新的。某个对象对静态数据成员的值更新一次,就可保证所有对象都可存取更新后的相同的值,这样可以提高时间效率。 静态数据成员的使用方法有:(1)静态数据成员在定义或说明时前面加关键字static;(2)静态数据成员的初始化与一般数据成员不同;(3)静态数据成员被存放在静态存储区;(4)引用静态数据成员的格式。 东华大学信息科学与技术学院 龚涛
在main()函数中,调用静态成员函数使用如下格式: <类名>::<静态成员函数名>(<参数表>) 5.5 静态成员 5.5.2 静态成员函数 静态成员函数和静态数据成员一样,都属于类,而不属于某个对象。因此,对静态成员的引用可以用类名。 在静态成员函数的实现中可以直接引用类中说明的静态成员,而不可以直接引用类中说明的非静态成员。静态成员函数中要引用非静态成员时,可通过对象来引用。 在main()函数中,调用静态成员函数使用如下格式: <类名>::<静态成员函数名>(<参数表>) 静态成员函数可以在没有定义 对象之前调用。 东华大学信息科学与技术学院 龚涛
5.5.2 静态成员函数 例5.11 分析该程序的输出结果。 int M::B=0; void main() 5.5.2 静态成员函数 例5.11 分析该程序的输出结果。 int M::B=0; void main() { M P(5), Q(10); M::f1(P); M::f1(Q); } #include <iostream.h> class M { public: M(int a) { A=a; B+=a; } static void f1(M m); private: int A; static int B; }; void M::f1(M m) cout<<"A="<<m.A<<endl; cout<<"B="<<B<<endl; } 东华大学信息科学与技术学院 龚涛
第5章 类和对象(一) 5.6 友元 只有类的成员函数才能访问该类的私有成员,程序中的其他函数是无法访问类中的私有成员的。非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递、类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。 为了解决上述问题,提出一种使用友元的方案。友元是一种说明在类体内的非成员函数,为了与该类的成员函数加以区别,在说明时前面加上关键字friend。友元不是成员函数,但是它可以访问类中的私有成员。友元可以是一个函数,称为友元函数。 东华大学信息科学与技术学院 龚涛
5.6 友元 5.6.1 友元函数 友元函数的特点是能够访问类中的私有成员和其他成员的非成员函数。友元函数在定义上和调用上与普通函数一样。 在例5.12中,该程序的Point类中说明了一个友元函数Distance(),在说明时前边加上friend关键字,标识它是友元函数。它的定义方法与普通函数定义一样,而不同于成员函数的定义,因为它不需要指出所属的类。但是,它可以引用类中的私有成员,它们是通过对象引用的。 本例中,p1.Getxy()和p2.Getxy()是成员函数的调用,要用对象来表示。而Distance(p1,p2)是友元函数的调用,它与普通函数的调用一样,不需要对象引用,它的参数是对象。 在例5.13中,该程序中有两个友元函数Time12()和Time24()。可见,它们的定义和调用与普通函数一样。 东华大学信息科学与技术学院 龚涛
5.6.1 友元函数 例5.13 分析该程序的输出结果。 #include <iostream.h> class Time 5.6.1 友元函数 例5.13 分析该程序的输出结果。 #include <iostream.h> class Time { public: Time(int new_hours, int new_minutes) { hours=new_hours; minutes=new_minutes; } friend void Time12(Time time); friend void Time24(Time time); private: int hours, minutes; }; void Time12(Time time) if(time.hours>12) time.hours-=12; cout<<time.hours<<":"<<time.minutes<<" PM"<<endl; } else cout<<time.hours<<":"<<time.minutes<<" AM"<<endl; void Time24(Time time) { cout<<time.hours<<":"<<time.minutes<<endl; } void main() Time Time1(20,30), Time2(10,45); Time12(Time1); Time24(Time1); Time12(Time2); Time24(Time2); 东华大学信息科学与技术学院 龚涛
在例5.14中,在X类中说明了Y类是它的友元类,因此,在Y类中的成员函数两次引用X类的私有成员a.x。友元的关系是不可逆的。 5.6 友元 5.6.2 友元类 友元除了友元函数,还可以是类,即一个类可以作为另一个类的友元。当一个类作为另一个类的友元时,就意味着这个类的所有成员函数都是另一个类的友元函数。 在例5.14中,在X类中说明了Y类是它的友元类,因此,在Y类中的成员函数两次引用X类的私有成员a.x。友元的关系是不可逆的。 东华大学信息科学与技术学院 龚涛
类的作用域简称类域,它是指在类的定义中由一对花括号括起来的部分。每一个类都具有该类的类域,该类的成员属于该类的类域中。 第5章 类和对象(一) 5.7 类的作用域 类的作用域简称类域,它是指在类的定义中由一对花括号括起来的部分。每一个类都具有该类的类域,该类的成员属于该类的类域中。 类域不同于文件域,在类域中说明的变量不能使用auto、register和extern等修饰符,只能用static修饰符,而说明的函数也不能用extern修饰符。另外,在类域中的静态成员和成员函数还具有外部的连接属性。 文件域可以包含类域,类域显然小于文件域。类域又比函数域大。 东华大学信息科学与技术学院 龚涛
第5章 类和对象(一) 5.8 局部类和嵌套类 5.8.1 局部类 在一个函数体内定义的类称为局部类。局部类中只能使用它的外围作用域中的对象和函数进行联系,因为外围作用域中的变量与该局部类的对象无关。在定义局部类时需要注意:局部类中不能说明静态成员函数,并且所有成员函数都必须定义在类体内。在实践中,局部类是很少使用的。 5.8.2 嵌套类 在一个类中定义的类称为嵌套类,定义嵌套类的类称为外围类。 定义嵌套类的目的在于隐藏类名,减少全局的标识符,从而限制用户能否使用该类建立对象。这样可以提高类的抽象能力,并且强调了两个类(外围类和嵌套类)的主从关系。 东华大学信息科学与技术学院 龚涛
1. 要上交的书面作业(写学号、姓名、题号和答案) 一、选择填空:1-10题 二、判断下列描述的正确性:1-10题 2. 上机实践题 第5章 类和对象(一) 1. 要上交的书面作业(写学号、姓名、题号和答案) 一、选择填空:1-10题 二、判断下列描述的正确性:1-10题 2. 上机实践题 三、分析下列程序的输出结果:1-5题 四、按下列要求编程:1-5题 3. 选做题 将你认为可疑的题目和结果报告给清华大学出版社,并请求解答,再告诉我。 东华大学信息科学与技术学院 龚涛
答疑联系信息 办公室电话:021-67792312-8040 手机:18201798064 E-mail:taogong@dhu.edu.cn 办公室地址:2号学院楼216室 东华大学信息科学与技术学院 龚涛