第4章面向对象的编程方法.docx
- 文档编号:17658935
- 上传时间:2023-07-27
- 格式:DOCX
- 页数:33
- 大小:25.53KB
第4章面向对象的编程方法.docx
《第4章面向对象的编程方法.docx》由会员分享,可在线阅读,更多相关《第4章面向对象的编程方法.docx(33页珍藏版)》请在冰点文库上搜索。
第4章面向对象的编程方法
第4章面向对象的编程方法
教学提示:
传统的结构化语言都是采用面向过程的方法来编写程序,在面向过程的程序设计方法中,代码和数据是分离的,因此程序的可维护性差,代码重复利用率低。
面向对象程序设计(ObjectOrientedPrograming)方法,是把数据和数据的处理方法封装在一个类中,实现了数据封装和数据抽象。
正是由于类的引入,使C++成为面向对象的程序设计语言。
教学目标:
掌握类和对象的定义,理解类的封装、类的抽象;理解类成员的三级存取方式;掌握声明和定义类的成员函数的方法,掌握访问成员函数的方法;理解构造函数和析构函数;熟练应用构造函数重载和友元。
4.1类与对象
4.1.1类的定义
C++的核心部分是类的定义,类定义体现了抽象数据类型的思想。
为达到信息隐蔽目的,C++规定类的成员有三级存取方式:
公有(public)、私有(private)和保护(protected)。
定义一个类,在public域中声明的数据成员和函数成员(成员函数),程序中其他类的对象或操作都能请求该类的对象执行它们,它们是公有的,这些数据成员和成员函数构成类的界面部分。
在private域和protected域中声明的数据成员和成员函数构成类的私有部分,只有该类的对象和成员函数以及声明为友元(friend)的函数或声明为友元类的对象才能访问它们。
在protected域中声明的数据成员和成员函数,还允许该类的派生类访问它们;而在private域中声明的数据成员和成员函数,不允许该类的派生类访问它们。
类的定义格式分为说明部分和实现部分。
说明部分用来说明该类的成员,实现部分是类的成员函数的定义。
类的定义格式如下:
class<类名>
{
public:
//公有数据成员和成员函数的说明
protected:
//保护数据成员和成员函数的说明
private:
//私有数据成员和成员函数的说明
};
//各成员函数的实现
class是类定义的关键字,类名是标识符,花括弧内是类的声明部分,类的成员函数的实现一般在“各成员函数的实现”部分,也可以在花括弧内声明成员函数的同时定义成员函数。
下面给出一个学生类的定义:
classstudent
{
private:
charname[20];
intage;
public:
voidstu(char*n,inta)
{
strncpy(name,n,19);//指针n指向的字符串赋值给name
age=a;
}
voidview()
{
cout<<"姓名:
"< cout<<"年龄: "< } }; 本例的所有成员函数的定义都是在类体内部实现的。 若在类体外实现,需要使用域运算符“: : ”,域运算符用来标识成员函数属于哪个类。 域运算符的使用格式是: <类名>: : <函数名>(<函数表>) { //实现部分 } 定义类时需要注意以下几点: (1)类体内不允许对所定义的数据成员进行初始化。 (2)类的数据成员类型可以是任意类型,也可以是另一个类的对象。 (3)自身的对象不能作为自己的数据成员,而自身的引用或者指针是可以的。 (4)如果用另一个类的对象来定义数据成员,且这个类的定义在后面,则必须提前声明。 4.1.2对象的定义 类是对一类对象的公共性质进行分析和认识,并用类抽象的方法加以描述。 对象是某个类的一个具体实例,对象的定义格式如下: <类名><对象名列表>; 其中,<对象名列表>可以是一个或者多个对象名。 当有多个对象名时,各对象名之间要用逗号分隔开。 对象可以是指向对象的指针或引用,也可以是对象数组。 例如,用上节定义的学生类可以定义学生对象: studentstul,stu2,*p; 对象成员有成员函数和数据成员,一般对象的成员表示方法如下: <对象名>.<成员名> 或者: <对象名>.<成员名>(<参数表>) 前者用于表示对象的数据成员,后者用来表示对象的成员函数。 例如,stu1的成员表示为: stul.name,stu1.age 它们分别表示student类的对象stul的name成员变量和age成员变量。 stul.view() 表示stul的成员函数view()。 指向对象的指针的成员表示方法如下: <对象指针名>-><成员名> 或者: <对象指针名>-><成员名>(<参数列表>) “->”是一个表示成员的运算符,它与前面讲过的“.”运算符的区别是: “->”用来表示指向对象的指针的成员,而“.”用来表示一般对象的成员;前者表示引用数据成员,而后者表示引用成员函数。 因此, <对象指针名>-><成员名> 与(*<对象成员名>).<成员名>是等价的。 例如,p的成员表示为: p->name,p->age 或者 (*p).name,(*p).age 这两种方式都可以用来表示指向对象指针p的数据成员。 用指向对象的指针p表示它的view()成员函数的方法如下: p->view()或者(*p).view() 下面的这个小程序定义了一个坐标类,它包含两个双精度型的成员变量x、y,和3个返回值为空的成员函数。 其中成员函数getx用来给x赋值,成员函数gety用来给y赋值;成员函数print用来在屏幕上输出x和y的值。 主函数中定义了类point的对象p,p引用了类的成员函数。 #include classpoint { private: doublex,y; public: voidgetx(doublepx) { x=px; } voidgety(doublepy) { y=py; } voidprint() { cout<<"x="< cout<<"y="< } }; voidmain() { pointp; p.getx(5.5); p.gety(6.6); p.print(); } 本程序的执行结果: x=5.5 y=6.6 补充例题: //4_bc2.cpp //本程序说明了对类的成员的访问。 #include usingnamespacestd; classmyclass{ inta;//私有数据(privatedata) public: intb;//公有数据(publicdata) voidsetab(inti);//公有函数(publicfunctions) intgeta(); voidreset(); }; voidmyclass: : setab(inti) { a=i;//直接访问成员变量a(referdirectlytoa) b=i*i;//直接访问成员变量b(referdirectlytob) } intmyclass: : geta() { returna;//直接访问成员变量a(referdirectlytoa) } voidmyclass: : reset() { setab(0);//直接调用函数setab(),已经知道该函数所属的对象 } intmain() { myclassob; ob.setab(5);//设置ob.a和ob.b cout<<"obaftersetab(5): "; cout< cout< cout<<'\n'; ob.b=20;//因为b公有,可直接访问 cout<<"obafterob.b=20: "; cout< cout< cout<<'\n'; ob.reset(); cout<<"obafterob.reset(): "; cout< cout< cout<<'\n'; return0; } 程序输出如下: obaftersetab(5): 525 obafterob.b=20: 520 obafterob.reset(): 00 现在让我们来仔细看看在程序中是如何访问类myc1ass的成员的。 首先,注意成员函数setab()给a和b赋值的方法,该函数使用了如下代码: a=i;//直接访问成员变量a(referdirectlytoa) b=i*i;//直接访问成员变量b(referdirectlytob) 由于setab()是成员函数,所以它可以直接访问成员变量a和b,而不需要显式地指定一个对象,也不需要使用点运算符。 前面已经解释过,成员函数总是通过对象来调用的,所以在调用了函数之后,也就知道了相应的对象。 因此,在成员函数中不需要再次指定对象。 因此,函数可以直接使用成员变量a和b。 接下来,注意,变量b是类myc1ass的公有成员。 这意味着类myclass之外的代码可以直接访问成员变量b。 在函数main()中,下面这行代码通过直接给变量b赋值说明了这一点。 ob.b=20;//因为成员变量b是公有的,所以可以直接访问 因为这行代码在类myclass之外,所以必须通过一个对象(在示例中是对象ob)和点运算符来访问成员变量b。 现在,我们来看看如何在main()中调用成员函数reset(),如下所示: ob.reset(); 因为reset()是类的公共成员函数,所以它可以通过一个对象(这里是对象ob)被类myclass之外的代码调用。 最后来看看函数reset()的代码。 由于resetl()是一个成员函数,所以它可以直接使用类的其他成员而无需使用点运算符或对象。 在上面的示例中,它调用了函数setab()。 因为已经知道了函数reset()对应的对象(因为通过这个对象调用了reset()),所以在函数中不需要再次指定对象。 理解这一点的关键在于: 当类的一个成员被类的外部代码访问时,必须同时指定一个对象。 然而,成员函数中的代码则可以直接使用类的其他成员。 注意: 如果你对如何访问类的成员仍然不是很有把握,也不必担心,在开始的时候要理解这点是有些困难,但只要你继续学习更多的示例程序,就会逐渐理解这些内容。 4.1.3内联函数 类的成员函数分为内联函数和外联函数。 类的成员方法可以声明和定义为内联函数。 内联函数是指定义在类体内的成员函数,即该函数的函数体在类体内部。 内联函数的语句一般只有1~5句,而且不允许使用switch语句。 在函数体内定义的函数,即使是没有明确的inline表示,也是内联函数。 定义在类体外的成员函数叫外联函数。 内联函数在调用时不像一般函数那样转去执行被调函数的函数体,执行完成后再转回调用函数中执行其后的语句,而是在调用内联函数的地方用内联函数体的代码直接替换调用语句,这样可以节省系统调用开销,提高运行速度。 当在类体的外部定义内联函数时,需把关键字inline加在函数定义之前。 例如: classpoint { private: doublex; doubley; public: voidsetpoint(doublepx,doublepy); }; inlinevoidsetpoint(doublepx,doublepy)//定义内联函数 { x=px; y=py; } 也可以把函数声明和定义合并放在类体内部,例如: classpoint { private: doublex; doubley; public: voidsetpoint(doublepx,doublepy) { x=px; y=py; } }; 4.2构造函数与析构函数 4.2.1构造函数 构造函数和析构函数是在类体中声明的两种特殊的成员函数。 构造函数完成类对象的初始化任务,它在对象定义时被自动调用。 构造函数的函数名与类的名称相同,性质与成员函数相同,但构造函数不能有返回值。 构造函数可以带参数,也可以重载。 对构造函数有如下几点说明: (1)构造函数是成员函数,函数体可以在类体内,也可以在类体外,在类体外要用类体名和域操作符“: : ”指明是哪个类的构造函数。 (2)构造函数是特殊的成员函数,函数名与类名相同且没有类型说明,它有隐含的返回值,该值由系统内部调用。 构造函数可以带一个或者多个参数。 (3)构造函数可以重载,可以定义多个参数个数或者参数类型不同的函数。 当程序中有多个构造函数时,编译程序为了确定调用哪一个构造函数需要把对象的参数和构造函数的参数进行比较,这和普通的函数重载选择过程相同。 (4)程序中不直接调用构造函数,构造函数在创建对象时由系统自动调用。 (5)在实际应用中,如果没有定义构造函数,则系统会生成一个默认的构造函数,该默认构造的函数没有参数,只是简单地把对象中的每个实例初始化为0。 构造函数是在定义对象的同时被调用的,形式如下: 类名对象名(实参表);//同时调用构造函数 下面是几个包含构造函数的类: (1)构造函数在类体内部 classpoint { private: intx; inty; public: point(intpx,intpy) { x=px; y=py; } }; 定义point的对象: pointpbegin(4,5); pointpend(104,105); (2)构造函数的函数体在类的外部: classstudent { public: voidview(); student(char*string,intnum); student(){};//构造函数可以重载,构造函数可以没有参数 private: charname[20]; intnumber; }; student: : student(char*string,intnum) { strcpy(name,string); number=num; } 定义类student的对象: studentstu1(“令狐冲”,1111); studentstu2(“任盈盈”,2222); (3)带默认参数的构造函数 classroom { private: floatlongth,highth,width; public: room(floatl=8,floath=3.5,floatw=6) { floatlongth=l; floathighth=h; floatwidth=w; } }; 构造函数room的3个参数均为默认参数,定义对象时可以省略实参,例如: roomr1(3.7,10,8); roomr2(7); roomr3(); 注意: 在重载没有实参的构造函数和有默认参数的构造函数时,有可能产生二义性,一旦产生二义性,编译系统将无法确定应当用哪个构造函数。 所以实际应用中应避免这种情况发生。 构造函数的构造顺序如下: 局部和静态对象成员以对象在类体中的声明顺序构造。 静态对象只被构造一次。 全局对象在main函数之前被构造。 在单文档程序中,按声明顺序构造;在多文档程序中,没有特殊顺序。 4.2.2析构函数 析构函数在对象生命期结束时由系统自动调用,主要用来释放用户通过构造函数向系统申请的存储空间,或关闭构造函数中打开的文件,它完成与构造函数相反的过程。 和构造函数一样,析构函数也是类中的特殊成员函数,它的函数名和类名相同,只在函数名头部比构造函数多了一个“~”字符。 析构函数没有参数,没有返回值,而且也不能重载,因而一个类中只能有一个析构函数。 析构函数的特点有以下几点: ●析构函数是成员函数,函数体可以在类体内部,也可以在类体外部。 ●析构函数是特殊的函数,它的名字与类同名,并在前面加“~”字符,用来和构造函数区别。 析构函数没有返回值,也没有参数。 ●一个类中只能定义一个析构函数。 ●析构函数可以被程序调用,也可以由系统调用。 在下面两种情况下,系统自动调用析构函数。 ◆如果一个对象被定义在函数体内部,则当这个函数结束时,该对象的析构函数被自动调用。 ◆如果一个对象是使用new运算符动态创建的,在使用delete运算符释放它时,系统自动调用析构函数,释放内存。 下面定义一个带构造函数和析构函数的学生(student)类。 构造函数使用new关键字为指向字符类型的指针变量name申请了20个字符的内存空间;析构函数使用delete关键字释放申请的内存空间。 classstudent { public: voidview(); student(); ~student(); private: intage; char*name; }; student: : student() { name=newchar[20]; cout<<"studenthasbeenconstructed"< } student: : ~student() { deletename; cout<<”studenthasbeendestructed"< } 上面这个例子是构造函数和析构函数的最常见用法,在构造函数中用操作符new为字符串分配存储空间,在析构函数中用delete释放存储空间。 析构函数和构造函数的构造顺序正好相反,先构造的后析构,后构造的先析构。 下面这个例子很清楚地说明了这一点。 //4_2.cpp #include classclassone { public: classone: : classone() { cout<<"constructingclassone"< } classone: : ~classone() { cout<<"destructingclassone"< } }; classclasstwo { public: classtwo: : classtwo() { cout<<"constructingclasstwo"< } classtwo: : ~classtwo() { cout<<"destructingclasstwo"< } }; voidmain() { classoneb1; classtwob2; } 程序运行结果为: constructingclassone constructingclasstwo destructingclasstwo destructingclassone ●关于对象赋值问题: 如果两个对象是同一个类型,那么其中的一个对象可以被赋给另一个对象。 有时候要用一个对象构造另一个对象,也可以说用一个对象的值初始化一个新构造的对象,这时候就需要拷贝构造函数,拷贝构造函数可以认为是构造函数的一个重载。 在C++中,如果用户没有提供拷贝构造函数,系统会自动提供一个默认的拷贝构造函数。 补充例题: 4_bc3.cpp //说明对象赋值 #include usingnamespacestd; classmyclass{ inta,b; public: voidsetab(inti,intj){a=i,b=j;} voidshowab(); }; voidmyclass: : showab() {
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 面向 对象 编程 方法
![提示](https://static.bingdoc.com/images/bang_tan.gif)