基于对象的程序设计.docx
- 文档编号:29064522
- 上传时间:2023-07-20
- 格式:DOCX
- 页数:64
- 大小:36.35KB
基于对象的程序设计.docx
《基于对象的程序设计.docx》由会员分享,可在线阅读,更多相关《基于对象的程序设计.docx(64页珍藏版)》请在冰豆网上搜索。
基于对象的程序设计
基于对象的程序设计
1.类
1.1.类的定义
类的定义包括两部分
类头:
由关键字class和后面的类名构成。
类体:
由花括号围起来。
类定义后面必须接一个分号或一列声明。
classScreen{/*...*/};
classScreen{/*...*/}myScreen,yourScreen;
classFirst{
//类成员
intmemi;
doublememd;
};
classSecond{
//类成员
intmemi;
doublememd;
};
//对象定义,两种定义方式
classFirstobj1;
Secondobj2;
obj2=obj1;//错误:
obj1和obj2类型不同
1.1.1.数据成员
数据成员的声明方式同变量相同。
#include
classScreen{
string_screen;//string(_height*_width)
string:
:
size_type_cursor;//当前屏幕Screen位置
short_height;//行数
short_width;//列数
};
数据成员可以是任何类型
classStackScreen{
inttopStack;
void(*handle)();
vector
};
除了静态(static)数据成员,数据成员不能在类体中被显示初始化。
classFirst{
intmemi=0;//错误
doublememd=0.0;//错误
};
1.1.2.成员函数
类的成员函数在类体中被声明。
classScreen{
public:
voidhome();
voidmove(int,int);
charget();
charget(int,int);
boolcheckRange(int,int);
//...
};
成员函数的定义可以被放在类体内。
classScreen{
public:
//home()andget()的定义
voidhome(){_cursor=0;}
charget(){return_screen[_cursor];}
//...
};
成员函数与普通函数不同:
1、成员函数在类域中声明,需要通过成员访问操作符引用。
ptrScreen->home();
myScreen.home();
2、成员函数拥有访问类的公有和私有成员的特权。
成员函数也可以重载。
classScreen{
public:
//重载成员函数get()的声明
charget(){return_screen[_cursor];}
charget(int,int);
//...
};
1.1.3.成员访问
通过访问限制符public、private和protected,C++提供了一种信息隐藏的机制。
●公有成员(public):
在程序的任何地方都可以被访问。
●私有成员(private):
只能被成员函数和类的友元访问。
●保护成员(protected):
在派生类就像公有成员,对其他程序则像私有成员。
classScreen{
public:
voidhome(){_cursor=0;}
charget(){return_screen[_cursor];}
charget(int,int);
voidmove(int,int);
//...
private:
string_screen;
string:
:
size_type_cursor;
short_height,_width;
};
1.1.4.友元
友元(friends)机制允许一个类授权其他的函数访问它的非公有成员。
classScreen{
friendistream&operator>>(istream&,Screen&);
friendostream&operator<<(ostream&,constScreen&);
public:
//...Screen类的其他部分
};
ostream&operator<<(ostream&os,constScreen&s)
{
//ok:
指向height,_width,和_screen
os<<"<"<
os< returnos; } 友元可以是名字空间函数、类的成员函数或完整的类。 1.1.5.类声明和类定义 声明指示Screen为一个类类型。 classScreen;//Screen类的声明 只有看到了类定义,才能把数据声明为该类的对象。 否则,只能声明为该类的指针或引用。 classScreen; classStackScreen{ inttopStack; //ok Screen*stack; void(*handler)(); }; 1.2.类对象 类的定义不会分配存储空间。 只有当定义一个类的对象时,系统才会分配存储空间。 classScreen{ public: //成员函数 private: string_screen; string: : size_type_cursor; short_height; short_width; }; intmain() { ScreenmainScreen; } 一个对象可以被同一类类型的另一个对象初始化或赋值。 缺省情况下,拷贝一个类对象与拷贝它的全部数据成员等价。 ScreenbufScreen=myScreen; //bufScreen._height=myScreen._height //bufScreen._width=myScreen._width //bufScreen._cursor=myScreen._cursor //bufScreen._screen=myScreen._screen 对象的指针和引用 intmain() { ScreenmyScreen,bufScreen[10]; Screen*ptr=newScreen; myScreen=*ptr; deleteptr; ptr=bufScreen; Screen&ref=*ptr; Screen&ref2=bufScreen[6]; } 我们必须用成员访问操作符来访问类对象的数据成员或成员函数。 点成员访问操作符(.)与类对象或引用联用;箭头访问操作符(->)与类对象的指针联用。 #include"Screen.h" boolisEqual(Screen&s1,Screen*s2) {//如果不相等返回false,相等则返回true if(s1.height()! =s2->height()|| s1.width()! =s2->width()) returnfalse; for(intix=0;ix for(intjy=0;jy if(s1.get(ix,jy)! =s2->get(ix,jy)) returnfalse; returntrue;//还在这里? 那就是相等. } 1.3.类成员函数 类成员函数是一组操作的集合,用户可以在该类的对象上执行这些操作。 classScreen{ public: voidhome(){_cursor=0;} voidmove(int,int); //… }; 注意: 虽然每个类对象都要自己的类成员拷贝,但是,每个类成员函数的拷贝只有一份。 1.3.1.inline和非inline成员函数 在类体内定义的类成员函数,被自动作为inline函数处理。 也可以显示地指定关键字inline。 classScreen{ public: //用inline关键字 //声明inline成员函数 inlinevoidhome(){_cursor=0;} inlinecharget(){return_screen[_cursor];} //... }; 类体外定义的成员函数 #include #include"Screen.h" //成员函数名用Screen: : 限定修饰 boolScreen: : checkRange(introw,intcol) {//validatecoordinates if(row<1||row>_height|| col<1||col>_width){ cerr<<"Screencoordinates(" < <<")outofbounds.\n"; returnfalse; } returntrue; } 类体外定义的函数不是inline函数,但可以显示地声明为inline函数。 inlinevoidScreen: : move(intr,intc) {//将_cursor称到绝对位置 if(checkRange(r,c))//位置合法吗? { introw=(r-1)*_width;//行位置 _cursor=row+c-1; } } //另一种方式 classScreen{ public: inlinecharget(int,int); //其他函数声明未变 }; charScreen: : get(intr,intc)//省略关键字inline { move(r,c);//_cursor位置 returnget();//另一个get()成员函数 } 由于内联函数必须在调用它的每个文本文件中被定义,所以没有在类体中定义的内联成员函数必须被放在类定义出现的头文件中。 例如,前面给出的move()和get()的定义应该被放在头文件Screen.h中,且跟在类Screen的定义后面。 1.3.2.访问类成员 无论成员函数是在类体内还是外面,我们都说它在类域内,这有两个含义: ●成员函数的定义可以引用任何一个类成员,无论该成员是私有的还是公有的,都不会破坏类访问限制。 ●成员函数可以直接访问它所属的类的成员,而无需使用点或箭头成员访问操作符。 #include voidScreen: : copy(constScreen&sobj) { //如果这个Screen对象与sobj是同一个对象 //则无需拷贝 if(this! =&sobj) { _height=sobj._height; _width=sobj._width; _cursor=0; //创建一个新字符串 //它的内容与sobj._screen相同 _screen=sobj._screen; } } #include"Screen.h" intmain() { Screens1; //设置s1的内容 Screens2; s2.copy(s1); //... } 1.3.3.特殊的成员函数 有一组特殊的成员函数用于管理类对象并处理诸如初始化、赋值、内存管理、类型转换及析构等活动。 这些函数通常由编译器隐式调用,如构造函数。 classScreen{ public: Screen(inthi,intwid,charbkground); //… }; intmain(){ Screens(24,80,‘*’); Screen*ps=newScreen(20,40,‘#’); //… } 1.3.4.const成员函数 任何试图修改const的动作都会被标记为编译错误: constcharblank=‘’; blank=‘\n’;//error 程序通常不直接修改类对象,而是通过调用公有成员函数来完成。 因此,为了尊重类的常量性,编译器必须区分安全与不安全的成员函数。 constScreenblankScreen; blankScreen.display();//读类对象 blankScreen.set('*');//错误: 修改类对象 函数只有被声明为const的成员函数才能被const类对象调用。 classScreen{ public: charget()const{return_screen[_cursor];} boolisEqual(charch)const; //... }; boolScreen: : isEqual(charch)const { returnch==_screen[_cursor]; } 把一个修改类数据成员的函数声明为const是非法的。 classScreen{ public: intok()const{return_cursor;} voiderror(intival)const{_cursor=ival;} //… private: string: : size_type_cursor; }; 把一个成员函数声明为const并不能阻止程序员可能做到的所有修改动作。 例如,若类含有指针,那么在const成员函数中就能修改指针所指向的对象,编译器不会检测这种错误。 编译器不会把这种修改检测为错误。 #include classText{ public: voidbad(conststring&parm)const; private: char*_text; }; voidText: : bad(conststring&parm)const { _text=parm.c_str();//error: 不能修改_text for(inti=0;ix _text[ix]=parm[ix];//不是错误,是不好的风格 const成员函数可以被相同参数表的非const成员函数重载。 classScreen{ public: charget(intx,inty); charget(intx,inty)const; //… }; 在这种情况下,类对象的常量性决定了该调用哪一个函数。 intmain(){ constScreencs; Screens; charch=cs.get(0,0);//callconstmember ch=s.get(0,0);//callnonconstmember } 注: 构造函数和析构函数可以被常量对象调用。 常量对象“从构造完成后到析构开始时刻”被认为是常量的。 1.3.5.mutable数据成员 当我们想监视一个类常量对象的内容时,可能会遇到一些问题。 例如, constScreencs(5,5); //想监视位置(3,4)的内容 cs.move(3,4); charch=cs.get(); 上述程序并不能工作,因为move()不是const成员函数, inlinevoidScreen: : move(intr,intc) { if(checkRange(r,c)) { introw=(r-1)*width; _cursor=row+c–1;//change_cursor } } 为了修改一个类的数据成员——即使它是const对象的成员,可以将该数据成员声明为mutable(易变的)。 mutable数据成员永远不会是const成员,即使它是一个const对象的数据成员。 mutable总可以被更新,即使是在一个const成员函数中。 classScreen{ public: //memberfunction private: string_screen; mutablestring: : size_type_cursor; short_height; short_width; }; 现在可以把成员函数move()声明为const,任何const成员函数都可以修改_cursor。 inlinevoidScreen: : move(intr,intc)const { //… _cursor=row+c–1; //… } 1.4.隐含的this指针 每个类对象都将维护自己的类数据成员的拷贝,但类成员函数只有一份拷贝。 成员函数如何确定操作哪个对象的数据? intmain(){ ScreenmyScreen(3,3),bufScreen; myScreen.clear(); myScreen.move(2,2); myScreen.set('*'); myScreen.display(); bufScreen.reSize(5,5); bufScreen.display(); } 每个成员函数都含有一个指向被调用对象的指针,这个指针被称为this。 因为this指针指向要调用其成员函数的类对象,所以如果函数move()被对象myScreen调用,this指针指向对象myScreen。 编译器为了实现this指针,对程序做了两处修改: 1、改变了成员函数的定义 //伪代码,说明编译器对一个成员函数定义的展开形式 //不是合法的C++代码 inlinevoidmove(Screen*this,intr,intc) { if(checkRange(r,c)) { introw=(r-1)*this->_width; this->_cursor=row+c-1; } } 2、改变了类成员函数的调用。 myScreen.move(2,2) //被转化为 move(&myScreen,2,2) 程序员可以在函数定义中显示地引用this指针。 inlinevoidScreen: : home() { this->_cursor=0; } 1.4.1.何时使用this指针 当我们需要返回被调用的对象时,需要使用this指针。 intmain(){ //... myScreen.clear().move(2,2).set('*').display(); bufScreen.reSize(5,5).display(); } //clear()的声明在类体内 //它指定了缺省实参bkground='#' Screen&Screen: : clear(charbkground) {//重置cursor以及清屏幕 _cursor=0; _screen.assign(//赋给字符串 _screen.size(),//size()个字符 bkground//值都是bkground ); //返回被调用的对象 return*this; } 注意: 该成员函数返回类型是引用Screen&,指向它自己所属类类型的对象。 this指针的另一种用法 voidScreen: : copy(constScreen&obj) { if(this! =&obj) { //… } } 1.5.静态类成员 某些类的所有对象可能都需要访问一个全局对象。 例如,要计数程序任意点共创建了多上该类型的对象,或者是指向该类型错误处理例程的指针。 在某些情况下,“提供一个所有对象共同使用的全局对象”比“每个类对象维持一个对立数据成员”更有效。 尽管该对象是全局对象,但她的存在只是为了支持该类对象。 静态数据成员被当作该类类型的全局对象。 对于非静态数据成员,每个类对象都有自己的拷贝。 而静态数据成员,对每个类类型只有一个拷贝静态数据成员。 只有一份由该类类型的所有对象共享访问。 静态数据成员与全局对象相比的两个优势: 1、静态数据成员没有进入程序的全局名字空间,不存在与程序中其他全局名字冲突的可能性。 2、可以实现信息隐藏。 静态成员可以是private成员,而全局对象不能。 static数据成员遵从public/private/protected访问规则。 classAccount{ public: Account(doubleamount,conststring&owner); stringowner(){return_owner;} private: staticdouble_interestRate; double_amount; string_owner; }; 所用Account的利率相同,被所有Account对象共享,所以把_interestRate声明为静态数据成员,减少每个Account所需的存储空间。 静态数据成员一般在类定义之外被初始化。 //静态类成员的显式初始化 #include"account.h" doubleAccount: : _interestRate=0.0589; 静态数据成员的初始化不应该被放在头文件中,而应该放在含有类的非inline函数定义的文件中。 #include classAccount{ public: //… private: staticconststringname; }; conststringAccount: : name(“SavingAccount”); 特例: 有序型(如整型
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 基于 对象 程序设计