类与对象.docx
- 文档编号:25224448
- 上传时间:2023-06-06
- 格式:DOCX
- 页数:76
- 大小:863.65KB
类与对象.docx
《类与对象.docx》由会员分享,可在线阅读,更多相关《类与对象.docx(76页珍藏版)》请在冰豆网上搜索。
类与对象
第12章类与对象
导读
类和对象是所有面向对象程序设计语言的核心概念。
本章主要介绍类和对象的基本概念及声明方法、类的public和private成员的访问控制、类的构造函数和析构函数的定义和使用,以及类的静态成员和友元的概念和相关应用、类的非静态成员函数特有的this指针。
最后简单介绍了一个标准类string,读者可以使用string类处理字符串,就象使用基本数据类型一样。
本章难度指数★★★★,教师授课8课时,学生上机练习8课时。
12.1类和对象的基本概念
我们生活在一个真实的世界中,周围能够感知的一切都是对象。
例如人、动物、植物、水、建筑、计算机等都是对象。
无论是有生命的对象(如人、动物等)还是无生命的对象(如房屋、计算机等),人们都是利用自己抽象思维能力,通过研究对象的静态属性和观察对象的动态行为而认识和区分对象的。
例如:
“张三”是一个大学生,他是客观存在的对象,他有学号、姓名、性别、年龄、专业、年级等静态属性,以及要进行上课、吃饭、运动和参加社团活动等动态行为。
类是现实世界中客观事物的抽象,即将具有相似静态属性和动态行为的对象集合的归纳为一个类。
例如,南开大学的其他大学生和“张三”都有相同的学号等静态属性和上课等动态行为,可以将所有南开大学的大学生归纳为一个类,这个类具有学号、姓名、性别、年龄、专业、年级等静态属性,和上课、吃饭、运动及参加社团活动等动态行为。
每一个同学都有这些属性和行为,但具体的取值会因人而异。
面向对象程序设计(OOP)就是用软件来模拟现实中的对象,在面向对象程序设计语言中仍然用类和对象这两个术语,类和对象是面向对象技术的核心。
类通过属性和行为来描述计算机要处理的对象集合的抽象概念,即把同一类对象共同具有的属性和行为封装在一起。
对象则是处理的具体对象。
类实际上就是数据类型,例如,整数也有一组属性和行为。
区别在于程序员定义类是为了与具体问题相适应,程序员可以通过增添他所需要的新数据类型来扩展这个程序设计语言。
12.2类的声明与定义
12.2.1类的声明
类实际上是用户根据问题的需要,自己定义的一种数据类型。
对象的属性在类中是通过数据成员来描述的,对象的行为则是通过函数成员(也称成员函数)来实现的。
类的声明形式为:
class<自定义类类型名>
{
public:
<公有成员说明表>
private:
<私有成员说明表>
};
例如,下面是日期类的声明。
classDate
{
public:
voidSetDate(int,int,int);//设置年、月、日
voidPrintDate();//输出日期
private:
intm_year;//年
intm_month;//月
intm_day;//日
};
上例声明了一个日期类,Date是类名,该类中的三个整型数据成员m_year、m_month和m_day分别用来表示日期的年、月和日属性;日期类的两个行为是设置日期和显示日期,由成员函数SetDate和PrintDate来描述。
类声明中的public和private,被称作访问说明符,用来指定类成员的访问级别。
关于类成员的访问控制在12.3.3节中将详细讲解。
12.2.2类的定义
在上面的类声明中,声明了类的名称、属性(成员数据)和行为(成员函数)。
但对于其中的成员函数只指明了它们的函数原型,而没有函数的定义。
也就是说,只在前面的类定义中告诉编译系统该类具有那些行为,但却没有说明该如何进行这些行为。
因此,需要对成员函数加以定义。
成员函数的定义形式如下:
<函数类型><类名>:
:
<函数名>(<形参数表>)
{
函数体
}
可以看出,成员函数与一般函数定义的不同之处是多了一个类名和作用域运算符“:
:
”,他们用来指明所定义函数是属于哪个类的,因此不同类中的成员函数可以重名。
例如,下面是日期类的声明与实现。
classDate
{
public:
voidSetDate(int,int,int);//设置年、月、日
voidPrintDate();//打印日期
private:
intm_year;
intm_month;
intm_day;
};
voidDate:
:
SetDate(inty,intm,intd)
{
m_year=y;
m_month=m;
m_day=d;
}
voidDate:
:
PrintDate()
{
cout< } 成员函数还可以在类中定义。 一般来说,在类中定义的成员函数规模都比较小,而且不允许使用switch语句。 这种在类定义中定义的成员函数,即便没有用inline来修饰,编译器也默认的把其视为内联函数。 在类中定义成员函数直接将成员函数声明改为成员函数的定义即可,不需要在函数名前添加“类名: : ”。 例如,下面是日期类的声明与实现,成员函数在类内定义。 classDate { public: voidSetDate(inty,intm,intd)//设置年、月、日 { m_year=y; m_month=m; m_day=d; } voidPrintDate()//打印日期 { cout< } private: intm_year; intm_month; intm_day; }; 12.3对象 12.3.1对象的声明 类是用户自定义的数据类型,与基本数据类型一样,需要声明类数据类型的变量,即对象,通过对一个个的实际对象实施不同的操作,来解决问题。 声明对象的过程叫做类的实例化。 先定义类类型,然后声明类对象的一般形式为: 类名对象名表; 例如,下面声明了两个Date类对象。 Datedate1,date2;//声明两个类对象date1和date2 与基本数据类型一致,date1和date2是Date类的两个变量,在此称为对象。 每一个对象都有自己独立的内存空间,存放各自的数据成员m_year、m_month和m_day。 可以通过sizeof(对象名)来求对象所占用的内存字节数。 12.3.2类成员的访问 在类体内,类的成员函数可以直接访问类中的任何成员。 例如,Date类中的SetDate成员函数和PrintDate成员函数可以直接访问数据成员m_year、m_month和m_day。 在类体外,则需要通过对象名与圆点成员访问运算符“.”一起使用,也可以使用指向对象的指针与箭头成员访问运算符“->”一起使用。 类成员的访问的形式如下: <对象名>.成员名 或 <指向对象的指针名>->成员名 【例12-1】成员访问运算符的使用。 完整的程序代码如下: //p12_1.cpp #include usingnamespacestd; //声明日期类 classDate { public: voidSetDate(int,int,int);//设置年、月、日 voidPrintDate();//打印日期 private: intm_year; intm_month; intm_day; }; //定义日期类 voidDate: : SetDate(inty,intm,intd) { m_year=y; m_month=m; m_day=d; } voidDate: : PrintDate() { cout< } intmain() { Datedate1,date2;//声明两个类对象date1和date2 Date*pDate=&date2;//指向date2的指针 date1.SetDate(2010,3,10);//调用成员函数SetDate,给date1对象设置时间 pDate->SetDate(2012,12,21); //使用指针调用成员函数SetDate,给date2对象设置时间 date1.PrintDate();//调用成员函数Print,显示date1对象的时间 pDate->PrintDate();//使用指针调用成员函数Print,显示date2对象的时间 return0; } 运行结果为: 2009年3月10日 2012年12月21日 12.3.3类成员的访问控制 C++是通过三个关键字public(公有)、private(私有)以及protected(保护)来指定类成员的访问限制的。 关键字public、private和protected被称为访问限定符。 公有成员: 在public(公有)区域内声明的成员是公有成员。 公有成员在程序的任何地方都可以被访问。 一般将公有成员限制在成员函数上,使其作为类与外界的接口,程序通过这种函数来操作该类的对象。 私有成员: 在private(私有)区域内声明的成员是私有成员。 私有成员只能被该类的成员函数或该类的友元访问。 一般将类的数据成员声明为private,使得程序必须通过类的成员函数才能访问数据成员,这样可以避免对成员数据的非法访问。 保护成员: 在protected(保护)区域内声明的成员是被保护的成员。 被声明为protected(保护)访问级别的数据成员或函数成员只能在该类的派生类类体中使用,这部分内容将在第13章继承中详细讲解。 一般情况下,将类的public成员放在前面,private成员放在尾部。 从一个访问控制符开始,它下面的所有成员数据和成员函数都被定义为该说明符所指定的访问级别,直到另一个访问说明符出现为止。 例如,上面的类Date中,成员函数SetDate(int,int,int)和PrintDate()同为public型;而成员数据m_year、m_month和m_day同为private型。 【例12-2】定义一个时间类,该类有“小时”和“分钟”两个属性,有设置时间和显示时间的行为,要求类中能够对设置的时间进行合法性检验。 算法分析: 合法的时间约束是: 小时的取值范围是0—24、分钟的取值范围是0-59。 类的使用者通过公有成员函数来设置时间和显示时间,对于时间的合法性检验应该在类内实现,所以声明为私有成员。 完整的程序代码如下: //p12_2 #include usingnamespacestd; classTime { public: voidSetTime(int,int); voidPrintTime(){cout< "< private: intm_hour; intm_minute; intTestTime(int,int); }; voidTime: : SetTime(inth,intm)//设置时间 { if(TestTime(h,m)) { m_hour=h; m_minute=m; } } intTime: : TestTime(inth,intm)//时间的合法性检验 { if(h<0||h>24||m<0||m>59)//判断时间的合法性 { cout<<"Thetimeiswrong! "< return0; } return1; } intmain() { Timet; t.SetTime(8,30);//调用公有成员函数完成对私有成员m_hour,m_minute的设置 t.PrintTime(); t.SetTime(35,30);//小时不合法,设置失败 t.PrintTime(); return0; } 运行结果为: 8: 30 Thetimeiswrong! 8: 30 为了进一步理解公有成员和私有成员的区别,如果将例12-2中的主程序代码修改为: intmain() { Timet; t.m_hour=8;//在类外直接为私有成员m_hour赋值 t.m_minute=30;//在类外直接为私有成员m_minute赋值 t.PrintTime(); t.TestTime(35,30);//在类外直接调用私有成员函数testTime() t.PrintTime(); return0; } 编译时会报如下错误: errorC2248: 'Time: : m_hour': cannotaccessprivatememberdeclaredinclass'Time' errorC2248: 'Time: : m_minute': cannotaccessprivatememberdeclaredinclass'Time' errorC2248: 'Time: : TestTime': cannotaccessprivatememberdeclaredinclass'Time' 由于数据成员m_hour、m_minute和成员函数TestTime在类内被声明为private类型,他们只能被类内的成员函数访问,如TestTime函数直接访问私有数据成员m_hour、m_minute,成员函数SetTime直接访问私有数据成员m_hour、m_minute和私有成员函数TestTime,PrintTime成员函数也直接访问私有数据成员m_hour、m_minute。 但主函数是类外的函数,所以不能访问类的私有成员。 将类的部分成员声明为私有成员,实现了信息的隐藏。 12.4构造函数和析构函数 12.4.1构造函数 对象就是类的一个变量,和其他变量一样,也可以在创建对象时为对象的数据成员赋初值。 在C++中,对象的初始化工作是由构造函数来完成的。 构造函数是一种特殊的成员函数,C++规定可以在类内声明一个或多个构造函数,以满足对象多样性的初始化需要。 构造函数具有如下特征: 1.构造函数名与类名相同。 2.构造函数无任何函数返回类型说明。 3.一个新的对象被创建时(通过对象声明语句或使用new运算符在堆区创建动态对象),属于该对象的构造函数被编译系统自动调用,完成该对象数据成员的初始化工作。 4.如果在类声明中没有给出构造函数,系统会自动给出一个默认的无参构造函数: <类名>(){} 5.如果在类中声明了多个构造函数,这就是构造函数的重载。 要求这些构造函数要有不同的参数表,系统自动调用构造函数时按照函数重载的规则选择其中的一个构造函数。 【例12-3】下面的代码说明了构造函数的用法。 完整的程序代码如下: //p12_3 #include usingnamespacestd; classTime { public: Time(int,int);//构造函数 voidSetTime(int,int); voidPrintTime(){cout< "< private: intm_hour; intm_minute; intTestTime(int,int); }; Time: : Time(inth,intm)//定义构造函数 { if(TestTime(h,m)) { m_hour=h; m_minute=m; } } voidTime: : SetTime(inth,intm)//设置时间 { if(TestTime(h,m)) { m_hour=h; m_minute=m; } } intTime: : TestTime(inth,intm)//时间的合法性检验 { if(h<0||h>24||m<0||m>59)//判断时间的合法性 { cout<<"Thetimeiswrong! "< return0; } return1; } intmain() { Timet(8,30);//系统自动调用构造函数,为对象数据成员初始化 t.PrintTime(); t.SetTime(12,30);//使用公有成员函数,为对象数据成员赋值 t.PrintTime(); return0; } 运行结果为: 8: 30 12: 30 下面是一个构造函数重载的实例。 【例12-4】定义一个整型数组类,要求根据需要确定数组的规模,默认数组的规模为10个元素。 完整的程序代码如下: //p12_4 #include usingnamespacestd; classIntArray { public: IntArray(int);//有参构造函数 IntArray();//无参构造函数 voidinfoOfArray() { cout<<"Thesizeofthisarrayis: "< } private: intm_size; int*m_vector; }; IntArray: : IntArray(intsz) { m_size=sz; m_vector=newint(sz); } IntArray: : IntArray() { m_size=10; m_vector=newint(m_size); } intmain() { IntArrayx,y(20); x.infoOfArray(); y.infoOfArray(); return0; } 运行结果为: Thesizeofarrayis: 10 Thesizeofarrayis: 20 上面代码中,IntArray类提供了有参与无参两个构造函数,这样就为用户提供了多种初始化类对象的方式。 如果需要指定数组的规模,则使用带参的构造函数初始化对象,若没有特殊要求,则使用无参的构造函数将对象初始化为默认的10个元素的规模。 12.4.2析构函数 在对象的生存期结束时,有时也需要执行一些操作。 如在例12-4中,创建一个IntArray类对象时,在构造函数中使用new分配了一个数组空间,当注销对象时,需要使用delete将动态分配的数组空间释放。 这部分工作就可以放在析构函数中。 析构函数是一个特殊的由用户定义的公有成员函数,析构函数具有如下特征: 1.析构函数名为: ~<类名>,如~IntArray。 2.构造函数无任何函数返回类型说明。 3.析构函数无参数,所以不能被重载。 4.如果在类声明中没有给出析构函数,系统会自动给出一个默认的析构函数: ~<类名>(){} 5.当对象的生命周期结束及用delete释放动态对象时,系统自动调用析构函数完成对象撤销前的处理。 【例12-5】在IntArray类中添加的析构函数,实现在撤销对象时,释放动态分配的数组空间的操作。 完整的程序代码如下: //p12_5 #include usingnamespacestd; classIntArray { public: IntArray(int); IntArray(); voidinfoOfArray() { cout<<"Thesizeofthisarrayis: "< } ~IntArray(); private: intm_size; int*m_vector; }; IntArray: : IntArray(intsz) { m_size=sz; m_vector=newint(sz); cout<<"ConstructingArraywithsize"< } IntArray: : IntArray() { m_size=10; m_vector=newint(m_size); cout<<"ConstructingArraywithsize"<<10< } IntArray: : ~IntArray() { cout<<"DestructingArraywithsize"< deletem_vector; } intmain() { IntArrayx,y(20),*p; p=newIntArray(30);//创建动态对象 x.infoOfArray(); y.infoOfArray(); p->infoOfArray(); deletep;//撤销动态对象 return0; } 运行结果为: ConstructingArraywithsize10 ConstructingArraywithsize20 ConstructingArraywithsize30 Thesizeofthisarrayis: 10 Thesizeofthisarrayis: 20 Thesizeofthisarrayis: 30 DestructingArraywithsize30 DestructingArraywithsize20 DestructingArraywithsize10 析构函数的功能不仅仅局限于释放资源上。 从更广泛的意义上来讲,类设计者可以利用析构函数来执行最后一次使用类对象后所做的任何操作。 12.5拷贝构造函数 C++中除普通的构造函数外,还有一类特殊的构造函数——拷贝构造函数。 拷贝构造函数的作用是用一个已经存在的对象来初始化一个正在创建的新对象。 拷贝构造函数有如下特征: 1.拷贝构造函数名与类名相同,形参只有一个,是对象的引用,所以,不能重载拷贝构造函数。 拷贝构造函数的原形为: <类名>(<类名>&对象名); 2.拷贝构造函数无任何函数返回类型说明。 3.如果在类声明中没有给出拷贝构造函数,系统会自动给出一个默认的拷贝构造函数,该拷贝构造函数只进行对象数据成员间的对位拷贝,即所谓的“浅拷贝”。 4.在某些情况下,用户必须在类定义中给出一个显式的拷贝构造函数,以实现用户指定的用一个对象初始化另一个对象的功能,即所谓的“深拷贝”。 5.在以下3种情况下,系统会自动调用拷贝构造函数: (1)当使用下面的声明语句用一个已存在的对象初始化一个新对象时,系统会自动调用拷贝构造函数:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 对象