




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、程序设计思想程序设计思想2主要的程序设计理念运算符重载运算符重载针对对象实现类似于原运算符的功能针对对象实现类似于原运算符的功能继承与组合继承与组合如何利用已有的类建立更复杂的对象如何利用已有的类建立更复杂的对象虚函数虚函数指针指向类族中的哪个对象,就调用该对象指针指向类族中的哪个对象,就调用该对象相应的方法成员相应的方法成员抽象类抽象类类中只规定有哪些行为,但并不具体实现类中只规定有哪些行为,但并不具体实现多态多态让同一种行为应用于各种对象、各种情况让同一种行为应用于各种对象、各种情况模板模板提高编码的效率提高编码的效率异常处理异常处理设置错误处理代码,应对可能出现的错误设置错误处理代码,应
2、对可能出现的错误constconst保护数据,防止被意外破坏保护数据,防止被意外破坏流流把不同类型的信息视为一个数据序列把不同类型的信息视为一个数据序列泛型泛型忽略数据元素在存储上的差异,强调行为上的忽略数据元素在存储上的差异,强调行为上的共性,强化代码的通用性共性,强化代码的通用性3类是规定行为的集合基本类型基本类型自定义类自定义类集合示例集合示例 intint CMyEmployee CMyEmployee元素示例元素示例 15152010053134,2010053134,张三张三, ,男男,1982.3.5.,1982.3.5. 运算运算算术运算算术运算 规定的行为规定的行为( (方法
3、方法) ) 类是基本数据类型的扩展,两者的核心都是集合及集类是基本数据类型的扩展,两者的核心都是集合及集合上的运算,基本类型的运算表现为合上的运算,基本类型的运算表现为“+”+”、“-”-”、“* *”、“/”/”等算术运算,是固有运算,可直接使用。等算术运算,是固有运算,可直接使用。 定义类中包含哪些数据元素相对简单。类的核心问题定义类中包含哪些数据元素相对简单。类的核心问题是定义类的方法成员,通过方法成员表现出类的元素是定义类的方法成员,通过方法成员表现出类的元素( (对象对象) )具有哪些行为。具有哪些行为。4对象的属性与操作 对象的行为通常可以分为对象的行为通常可以分为计算类计算类和和
4、非计算类非计算类。非计算。非计算类包括显示、存储、传输等行为,比如关于图形处理建立类包括显示、存储、传输等行为,比如关于图形处理建立的区域类族大体上会有三角形、方形、多边形等自定义类,的区域类族大体上会有三角形、方形、多边形等自定义类,各个对象都有在屏幕上画出该形状这一行为,而且通常都各个对象都有在屏幕上画出该形状这一行为,而且通常都用同一个成员名来描述这个行为,如用同一个成员名来描述这个行为,如“对象对象.Draw( )”.Draw( )”。这一现象将在下一节这一现象将在下一节“抽象类抽象类”当中讨论。当中讨论。 计算类计算类行为是指获取对象的相关信息,或者通过对象行为是指获取对象的相关信息
5、,或者通过对象及其它数据计算得到一些信息。比如上述关于区域的类族,及其它数据计算得到一些信息。比如上述关于区域的类族,各个对象通常都需要对外公布各个对象通常都需要对外公布( (允许外部访问,允许外部访问,public)public)宽宽度和高度两个数据,这样的信息称为度和高度两个数据,这样的信息称为“对象的属性对象的属性”,通,通常也有相对固定的成员名,例如取对象的宽度通常写作常也有相对固定的成员名,例如取对象的宽度通常写作“对象对象.GetWidth.GetWidth( )”( )”。这一类行为当中包含运算符重载。这一类行为当中包含运算符重载。5为什么要有运算符重载? 起因起因( (在上一单
6、元已述,重现在上一单元已述,重现) ):(1) (1) 常规的运算符只运用于基本数据类型,并且有固常规的运算符只运用于基本数据类型,并且有固定的用法,基本上与数学上的用法一致。定的用法,基本上与数学上的用法一致。好记好记(2) (2) 类是数据与处理类是数据与处理( (称为称为“方法方法”) )的结合体,有很的结合体,有很多处理与常规的运算有直接联系,或者是常规运算在多处理与常规的运算有直接联系,或者是常规运算在意思上的延伸,比如意思上的延伸,比如CStringCString类的加法延伸为拼接。类的加法延伸为拼接。好理解好理解(3) (3) 设计一个用于实现加法的函数设计一个用于实现加法的函数
7、AddAdd,带有两个入口,带有两个入口参数分别记作参数分别记作a a和和b b,则调用该函数写作,则调用该函数写作“Add(a,bAdd(a,b)”)”,这一写法当然不如这一写法当然不如“a+ba+b”更简洁易懂。更简洁易懂。好写好写 目标:目标:希望为类设计一个与常规运算符在意义上接近的处理希望为类设计一个与常规运算符在意义上接近的处理或者计算,并且沿用常规运算符原有的写法或者计算,并且沿用常规运算符原有的写法6运算符重载方式重载方式重载方式用成员函用成员函数重载数重载用友元函用友元函数重载数重载特特点点(1)(1)“=”=”等少量的几个运算符必须用成等少量的几个运算符必须用成员函数的方式
8、重载员函数的方式重载(2)(2)重载二元运算符时,右侧的操作数重载二元运算符时,右侧的操作数可以是各种类型,但左侧的操作数可以是各种类型,但左侧的操作数必须是类的对象必须是类的对象(3)(3)成员函数的形参数目等于运算符操成员函数的形参数目等于运算符操作数的数量减作数的数量减1 1(1)(1)通常用全局函数实现通常用全局函数实现(2)(2)需要访问类的私有成员时才需要定需要访问类的私有成员时才需要定义成友元函数义成友元函数7不能重载的运算符 以下运算符不能被重载:以下运算符不能被重载:成员访问成员访问. .域限制符域限制符:条件运算条件运算? :取字节数取字节数sizeofsizeof8经常被
9、重载的运算符运算符运算符 分类分类 重载方式重载方式+ +,- -,* *,/ /,% %算术运算算术运算两种均可两种均可,=,=,-下标下标, ,指向指向成员函数成员函数移位移位/ /流流两种均可两种均可newnew与与deletedelete内存管理内存管理两种均可两种均可 特别地,特别地,C+C+规定圆括号规定圆括号“( )”( )”也可以重载也可以重载已述,本节将只讲解已述,本节将只讲解+、 和和( )( )这几个这几个运算符的重载运算符的重载9为CClock类定义前置“+”前述前述CClockCClock类的声明如下:类的声明如下:class CClockclass CClock p
10、ublic:public: CClock(int h,int m,int CClock(int h,int m,int s); s); CClockCClock( );( ); int GetHour int GetHour( );( );/取小时数取小时数 int GetMinitint GetMinit( );( );/取分钟数取分钟数 CStringCStringGetTimeGetTime( );( ); /取当前时间字符串取当前时间字符串 void Stepupvoid Stepup( );( );/令时钟走一步令时钟走一步(1(1秒秒) ) CClockCClock operato
11、r+( ); operator+( );private:private: int m_hour , m_minit , m_second int m_hour , m_minit , m_second; ;/时分秒时分秒;CClockCClock类的对象是时钟,类的对象是时钟,“+”+”的功能显而易的功能显而易见。用成员函数为见。用成员函数为CClockCClock类定义前置类定义前置“+”+”,首先需要在类中添加相应的函数声明。首先需要在类中添加相应的函数声明。注意,类中已有注意,类中已有StepupStepup函数可以利用函数可以利用10编写“operator+”利用已有的利用已有的Ste
12、pupStepup函数,前置自增功能很容易实现:函数,前置自增功能很容易实现:CClock CClock:operatorCClock CClock:operator+( )+( ) StepupStepup( );( );return return * *this;this; C+C+以以“是否有形参是否有形参”来区分前置自增和后置自增,来区分前置自增和后置自增,后置自增的重载函数如下:后置自增的重载函数如下:CClock CClock:operator+(intCClock CClock:operator+(int) ) CClockCClock x( x(* *this);this);/
13、创建临时对象创建临时对象StepupStepup( );( );/即即this-Stepupthis-Stepup( )( )return x;return x; 注意:注意:(1)(1)后置自增函数带有一个后置自增函数带有一个intint型形参,这是型形参,这是区分前置后置的标记,并无其它含义,函数体内也区分前置后置的标记,并无其它含义,函数体内也不使用该参数值,甚至可以没有形参的名字;不使用该参数值,甚至可以没有形参的名字;(2)(2)不不要忘记在类中添加相应的函数声明要忘记在类中添加相应的函数声明11友元函数定义前置“+”CClockCClock类的声明如下:类的声明如下:class C
14、Clockclass CClock public:public: CClock(int h,int m,int CClock(int h,int m,int s); s); CClockCClock( );( ); int GetHour int GetHour( );( );/取小时数取小时数 int GetMinitint GetMinit( );( );/取分钟数取分钟数 CStringCStringGetTimeGetTime( );( ); /取当前时间字符串取当前时间字符串 void Stepupvoid Stepup( );( );/令时钟走一步令时钟走一步(1(1秒秒) ) f
15、riend CClock operator+(CClockfriend CClock operator+(CClock &x); &x);private:private: int m_hour , m_minit , m_second int m_hour , m_minit , m_second; ;/时分秒时分秒;CClockCClock operator+(CClockoperator+(CClock &x)&x) c.Stepupc.Stepup();();return x;return x; 12友元函数定义前置“+”这是前置自增的友元函数声明:这是前
16、置自增的友元函数声明:friend CClock operator+(CClockfriend CClock operator+(CClock & &x);x);后置自增的友元函数声明为:后置自增的友元函数声明为:friend CClock operator+(CClockfriend CClock operator+(CClock & &x,intx,int););相应的函数代码:相应的函数代码:CClock operator+(CClockCClock operator+(CClock & &x,intx,int) ) CClock m(xC
17、Clock m(x););/记得吗记得吗, ,这将调用拷贝构造函数这将调用拷贝构造函数c.Stepupc.Stepup( );( );return m;return m; 最初编写代码的时候忘记了引最初编写代码的时候忘记了引用,没有引用是不行的用,没有引用是不行的13为CClock定义下标 设设x x是是CClockCClock类的对象,即一个时钟,原本类的对象,即一个时钟,原本x?x?是没有意义的,但不妨人为地做如下定义:是没有意义的,但不妨人为地做如下定义:x0 - x0 - 时钟的当前小时数时钟的当前小时数x1 - x1 - 时钟的当前分钟数时钟的当前分钟数x2 - x2 - 时钟的当前
18、秒数时钟的当前秒数xixi - -1 - -1,i i不是不是0 0、1 1、2 2时时 根据运算符重载的有关规则,下标运算根据运算符重载的有关规则,下标运算“ ” ”必须用成员函数实现,则在写法上必须用成员函数实现,则在写法上“xixi”是函数调是函数调用用“x.operator(ix.operator(i)”)”的变形,的变形,“k=xik=xi”也可以也可以写成:写成:k = x.operator(ik = x.operator(i) )14为CClock类定义“ ”在在CClockCClock类中添加关于类中添加关于 的成员函数如下:的成员函数如下:class CClockclass
19、CClockpublic:public: CClock(int h,int m,int CClock(int h,int m,int s); s); CClockCClock( );( ); int GetHour int GetHour( );( );/取小时数取小时数 int GetMinitint GetMinit( );( );/取分钟数取分钟数 CStringCStringGetTimeGetTime( );( ); /取当前时间字符串取当前时间字符串 void Stepupvoid Stepup( );( );/令时钟走一步令时钟走一步(1(1秒秒) ) CClockCClock
20、operator( ); operator( );private:private: int m_hour , m_minit , m_second int m_hour , m_minit , m_second; ;/时分秒时分秒;15CClock类的“operator ”int CClock:operator(intint CClock:operator(int i) i) if(iif(i=0)=0)return m_hourreturn m_hour; ;if(iif(i=1)=1)return m_minitreturn m_minit; ;if(iif(i=2)=2)return m
21、_secondreturn m_second; ;return -1;return -1;16重载“ ”的意义 设设x x是一个对象,通过运算符重载,可以把原本是一个对象,通过运算符重载,可以把原本没有意义的写法没有意义的写法“x?”x?”赋予确定的含义,这与前述赋予确定的含义,这与前述的运算符重载有着重大差异。的运算符重载有着重大差异。 既然如此,对于任意一个允许重载的运算符,也既然如此,对于任意一个允许重载的运算符,也可以赋予它与原运算符完全没有关联的功能。但是极可以赋予它与原运算符完全没有关联的功能。但是极少有人这样用,因为运算符重载的目的在于借用运算少有人这样用,因为运算符重载的目的在
22、于借用运算符原有的含义、用原有的写法实现对象的某些处理功符原有的含义、用原有的写法实现对象的某些处理功能,如果这些功能与被重载的运算符相去甚远,则重能,如果这些功能与被重载的运算符相去甚远,则重载的效果只会让人造成概念上的混乱,不利于软件开载的效果只会让人造成概念上的混乱,不利于软件开发。正如在定义一个函数时,通常都以与函数功能相发。正如在定义一个函数时,通常都以与函数功能相关的英文单词或者编写作为函数名。关的英文单词或者编写作为函数名。17CClock类的“operator( )” 对于对象对于对象x x,运算符重赋予了,运算符重赋予了“x?”x?”一定的意一定的意义,对于同样原本没有意义的
23、义,对于同样原本没有意义的“x(?)”x(?)”也可以依照此也可以依照此例处理。例处理。 实际上,把对实际上,把对“ ” ”重载时所有的重载时所有的“”换成换成“(”(”、所有的、所有的“”换成换成“)”)”,前述代码同样可以,前述代码同样可以编译通过。编译通过。 很多资料上把很多资料上把“operator( )”operator( )”称为函数调用运称为函数调用运算符,这容易在概念上造成混乱。建议:不用管它叫算符,这容易在概念上造成混乱。建议:不用管它叫什么名字,因为对于对象什么名字,因为对于对象x x而言,而言,“x(?)”x(?)”原本没有原本没有意义,现在通过运算符重载规定了该写法的含
24、义。意义,现在通过运算符重载规定了该写法的含义。18继承还是组合 面向对象程序设计的基本设计单位是面向对象程序设计的基本设计单位是“类类”,类的,类的本质是规定了对象的数据信息和行为。设计更复杂的程本质是规定了对象的数据信息和行为。设计更复杂的程序时,可以利用已有的类,在利用方法上就有了本页的序时,可以利用已有的类,在利用方法上就有了本页的标题:继承还是组合?标题:继承还是组合?本节只考虑本节只考虑publicpublic继承继承 设设X X和和Y Y是两个类,是两个类,x x是是X X的对象,的对象,y y是是Y Y的对象的对象继承继承(“is a”(“is a”关系关系) )如果如果X X
25、是是Y Y的派生类,则:的派生类,则:x is a y.x is a y.( (见下页图元类族示例见下页图元类族示例) )组合组合(“has a”(“has a”关系关系) )如果如果y y是是x x的一个子对象,则:的一个子对象,则:x has a y.x has a y.例如:例如:CMyEmployeeCMyEmployee类中包含两个类中包含两个CStringCString子对象,子对象,The employee The employee has ahas a number. number.The employee The employee has ahas a name. name.
26、19继承描述“is a”关系Point(Point(点点) is a element() is a element(图元图元) )RigionRigion( (区域区域) is a element() is a element(图元图元) )Circle(Circle(方形方形) is a Rigion) is a Rigion( (区域区域) ). . .图元图元颜色颜色, ,尺寸尺寸, ,边界边界画图画图, ,擦除擦除, ,取边界取边界点点线线起点起点, ,终点终点, ,线型线型区域区域填充模式填充模式, ,透明度透明度直线直线弧线弧线. . .曲线曲线. . .方形方形. . .圆形圆形
27、. . .多边形多边形. . .注意,不论是继承还是组合,在注意,不论是继承还是组合,在x x中都包含一个中都包含一个y y的对的对象作为象作为x x的一部分!的一部分!继承与组合到底有什么差别?继承与组合到底有什么差别?20继承与组合的差异x is a y(x is a y(继承继承) )(1) (1) 编写编写X X类的成员函数类的成员函数代码时,可以访问代码时,可以访问y y的的publicpublic成员和成员和protectedprotected成员,包括方法成员也包成员,包括方法成员也包括数据成员括数据成员(2) (2) 在类的外部编写代码在类的外部编写代码时,可以访问时,可以访问
28、x x的的publicpublic成员,也能访问成员,也能访问y y的的publicpublic成员成员(3) CX(3) CX的构造函数以的构造函数以“:CY(.)”:CY(.)”指明如何调指明如何调用基类的构造函数用基类的构造函数x has a y(x has a y(组合组合) )(1) (1) 编写编写X X类的成员函数代类的成员函数代码时,只能访问码时,只能访问y y的的publicpublic成员,包括方法成员和数成员,包括方法成员和数据成员据成员(2) (2) 在类的外部编写代码在类的外部编写代码时,只能访问时,只能访问x x的的publicpublic成成员员(3) CX(3)
29、 CX的构造函数以的构造函数以“: :子子对象名对象名(.)”(.)”指明如何调指明如何调用子对象的构造函数用子对象的构造函数21测试1,派生类内部CYCY类的声明:类的声明:class CYclass CYpublic:public: f1( ); f1( ); int int d1; d1;protected:protected: f2( ); f2( ); int int d2; d2;private:private: f3( ); f3( ); int int d3; d3; CXCX类的声明:类的声明:class CX:publicclass CX:public CY CY /继承继
30、承public:public: f_x1( ); f_x1( ); int int d_x1; d_x1;protected:protected: f_x2( ); f_x2( ); int int d_x2; d_x2;private:private: f_x3( ); f_x3( ); int int d_x3; d_x3; 编写编写CXCX的函数代码时,允许访问的函数代码时,允许访问CXCX和和CYCY的哪些成员?的哪些成员?22测试2,派生类外部CYCY类的声明:类的声明:class CYclass CYpublic:public: f1( ); f1( ); int int d1;
31、d1;protected:protected: f2( ); f2( ); int int d2; d2;private:private: f3( ); f3( ); int int d3; d3; CXCX类的声明:类的声明:class CX:publicclass CX:public CY CY /继承继承public:public: f_x1( ); f_x1( ); int int d_x1; d_x1;protected:protected: f_x2( ); f_x2( ); int int d_x2; d_x2;private:private: f_x3( ); f_x3( );
32、 int int d_x3; d_x3; x x是是CXCX的对象,的对象,“x.?”x.?”是合法的访问?是合法的访问?23测试3,组合类内部CYCY类的声明:类的声明:class CYclass CYpublic:public: f1( ); f1( ); int int d1; d1;protected:protected: f2( ); f2( ); int int d2; d2;private:private: f3( ); f3( ); int int d3; d3; 编写编写CXCX的函数代码时,允许访问的函数代码时,允许访问CXCX和和CYCY的哪些成员?的哪些成员?CXCX类
33、的声明:类的声明:class CXclass CX /组合组合public:public: f_x1( ); f_x1( ); CY y1; CY y1;protected:protected: f_x2( ); f_x2( ); CY y2; CY y2;private:private: f_x3( ); f_x3( ); CY y3; CY y3; 访问访问CYCY的成员时,必须指明访问哪一个子对象的成员,比的成员时,必须指明访问哪一个子对象的成员,比如如“y1.f1( )”y1.f1( )”、“y2.f1( )”y2.f1( )”、“y3.d1=5”y3.d1=5”24测试4,组合类外部
34、CYCY类的声明:类的声明:class CYclass CYpublic:public: f1( ); f1( ); int int d1; d1;protected:protected: f2( ); f2( ); int int d2; d2;private:private: f3( ); f3( ); int int d3; d3; CXCX类的声明:类的声明:class CXclass CX /组合组合public:public: f_x1( ); f_x1( ); CY y1; CY y1;protected:protected: f_x2( ); f_x2( ); CY y2; C
35、Y y2;private:private: f_x3( ); f_x3( ); CY y3; CY y3; x x是是CXCX的对象,的对象,“x.?”x.?”是合法的访问?是合法的访问?x.f_x1( )x.f_x1( )x.y1.f1( )x.y1.f1( )x.y1.d1=5;x.y1.d1=5;25多用组合少用继承 如本页标题所示,在建立新的类时,多用组合少如本页标题所示,在建立新的类时,多用组合少用继承。并且把子对象置于用继承。并且把子对象置于privateprivate保护之下。保护之下。原因:原因: 面向对象的核心思想之一是封装,即允许外部访面向对象的核心思想之一是封装,即允许外
36、部访问对象的哪些成员。以继承的方式建立新的类,编写问对象的哪些成员。以继承的方式建立新的类,编写代码的人往往容易忽略可以从外部访问基类的代码的人往往容易忽略可以从外部访问基类的publicpublic成员,从而导致一些预料之外的信息暴露。成员,从而导致一些预料之外的信息暴露。 当然,最根本的还是根据当然,最根本的还是根据“is a”is a”还是还是“has a”has a”关系来选用继承和组合。关系来选用继承和组合。26继承导致类族转换话题:转换话题: 面向对象的程序设计模式中经常会设计一系列有面向对象的程序设计模式中经常会设计一系列有继承与派生关系的类,从一个基类开始往下派生出的继承与派生
37、关系的类,从一个基类开始往下派生出的所有的类形成一个所有的类形成一个“类族类族”。类族的设计思想显然是。类族的设计思想显然是为了代码重用。为了代码重用。 在一个类族中,对象尽管属于不同的类,但通常在一个类族中,对象尽管属于不同的类,但通常都有一些相同的特征或者行为。即:类族中任意一个都有一些相同的特征或者行为。即:类族中任意一个类的对象都拥有基类的数据成员类的对象都拥有基类的数据成员( (虽然有可能因为继虽然有可能因为继承方式而不能访问承方式而不能访问) ),视作具有相同的特征信息;类,视作具有相同的特征信息;类族中的对象通常也具有一些同名的函数成员,视作具族中的对象通常也具有一些同名的函数成
38、员,视作具有相同的行为,这些函数的功能相同或相似,但实现有相同的行为,这些函数的功能相同或相似,但实现的具体代码可以不同。的具体代码可以不同。27类族对象的共性 例如,以例如,以“图元图元”为基类的类族具有颜色、尺寸等为基类的类族具有颜色、尺寸等共同的数据成员,以及画、擦等同名的方法成员;除此共同的数据成员,以及画、擦等同名的方法成员;除此之外,以之外,以“区域区域”为基类的类族还具有填充模式、透明为基类的类族还具有填充模式、透明度这两个共同的数据成员。度这两个共同的数据成员。图元图元颜色颜色, ,尺寸尺寸, ,边界边界画图画图, ,擦除擦除, ,取边界取边界点点线线起点起点, ,终点终点,
39、,线型线型区域区域填充模式填充模式, ,透明度透明度直线直线弧线弧线. . .曲线曲线. . .方形方形. . .圆形圆形. . .多边形多边形. . .28对图元类族确定名称 为了后续叙述的方便,为各个类及各个成员命名为了后续叙述的方便,为各个类及各个成员命名CElementCElementcolor,size,bordercolor,size,borderDraw,EraseDraw,Erase,.,.CPointCPointCSegmentCSegmentstart,end,stylestart,end,styleCAreaCAreapattern,transparencypattern
40、,transparencyCLineCLineCArcCArc. . .CCurveCCurve. . .CSquareCSquare. . .CCircleCCircle. . .CPolygonCPolygon. . .29创建对象与调用方法 设有如下的创建对象:设有如下的创建对象:CElementCElement x1; x1;/图元图元CAreaCArea x2;x2;/区域区域CSegmentCSegment x3; x3;/线线CCircleCCircle x4; x4;/圆圆则,以下调用非常明确是调用哪个对象的哪个方法:则,以下调用非常明确是调用哪个对象的哪个方法:x1.Draw
41、( );x1.Draw( );x2.Draw( );x2.Draw( );x3.Draw( );x3.Draw( );x4.Draw( );x4.Draw( );原本没有疑问的用法会因为指针而产生歧义,见下页。原本没有疑问的用法会因为指针而产生歧义,见下页。30重要规则C+C+规定:规定: 派生类的地址可以赋值给指向基类的指针变量派生类的地址可以赋值给指向基类的指针变量你如何理解这个规则?请先看下面的例子你如何理解这个规则?请先看下面的例子为了说明问题的简便,改用下面的最简单的类声明:为了说明问题的简便,改用下面的最简单的类声明:class CAclass CApublic:public:vo
42、id fun( )void fun( )coutcoutFun of CAn; ;Fun of CAn; ;class CB:publicclass CB:public CA CApublic:public:void fun( )void fun( ) coutcoutFun of CBn; ;Fun of CBn; ;void fb( ) coutThis is fbnvoid fb( ) coutfun( );p-fun( );p-fbp-fb( );( );q-fun( );q-fun( ); 派生类地址可以赋值给指向派生类地址可以赋值给指向基类的指针变量,反之不行基类的指针变量,反之不
43、行这一行是对的,不用写这一行是对的,不用写“p=(CAp=(CA* *)&y;”)&y;”p p的类型决定了通过的类型决定了通过p p只能访问只能访问CACA的成的成员,员,fbfb是是CBCB的成员但不是的成员但不是CACA的成员的成员32指针指向类族中的对象指出下面代码中的运行结果:指出下面代码中的运行结果: main( )main( ) CA x,CA x,* *q;q;CB y;CB y;x.funx.fun( );( );q=&x;q=&x;q-fun( );q-fun( );y.funy.fun( );( );q=&y;q=&y;q-
44、fun( );q-fun( ); 最后一行显示值得探讨:最后一行显示值得探讨:q q是指是指向基类向基类CACA的指针变量,但此时的指针变量,但此时指向派生类对象指向派生类对象y y,“q-q-fun( )”fun( )”到底调用哪个函数?到底调用哪个函数?另外,编程者希望它调用哪个另外,编程者希望它调用哪个函数?函数?33再谈规则上述规则的意义:上述规则的意义:(1)(1)因为代码重用而产生了类族,类族中的对象都有因为代码重用而产生了类族,类族中的对象都有相同或者相似的行为。在编程者看来,类族中相同或者相似的行为。在编程者看来,类族中的对象都是的对象都是“差不多差不多”的。的。(2)(2)编
45、程者希望借用一个指针变量,不论该指针指向编程者希望借用一个指针变量,不论该指针指向类族中的哪一个对象,都能正确地访问相应的类族中的哪一个对象,都能正确地访问相应的成员。如果派生类对基类的某个方法成员编写成员。如果派生类对基类的某个方法成员编写了新代码了新代码( (覆盖覆盖) ),则希望指针指向派生类对象,则希望指针指向派生类对象时,能够访问新代码。时,能够访问新代码。(3)(3)以以“基类基类* * q” q”定义指针变量,则通过定义指针变量,则通过q q只能访问只能访问基类的成员,而不能访问派生类新增的成员,基类的成员,而不能访问派生类新增的成员,从而保证不会出现访问一个不存在的成员的现从而
46、保证不会出现访问一个不存在的成员的现象。象。派生类地址可以赋值给指向基类的指针变派生类地址可以赋值给指向基类的指针变量,反之不行量,反之不行前述示例说明没能做到这一点前述示例说明没能做到这一点34虚函数虚函数专门用于解决上述问题:虚函数专门用于解决上述问题: 如前例,基类中定义了方法成员如前例,基类中定义了方法成员funfun,派生类更,派生类更新了该方法。以新了该方法。以“CACA* * q;” q;”定义指针变量,希望当定义指针变量,希望当q q指向基类指向基类CACA的对象时,的对象时,“q-fun( )”q-fun( )”访问基类访问基类CACA的的成员函数成员函数funfun;当;当
47、q q指向派生类指向派生类CBCB的对象时,的对象时,“q-q-fun( )”fun( )”访问派生类访问派生类CBCB更新之后的成员函数更新之后的成员函数funfun。 简言之:希望简言之:希望q q指向谁就调用谁的成员函数指向谁就调用谁的成员函数对于上述需求,需要在基类中把该方法成员定义成对于上述需求,需要在基类中把该方法成员定义成“虚函数虚函数”。声明虚函数的格式如下:。声明虚函数的格式如下:virtualvirtual 返回值类型返回值类型 函数名函数名( (形参表形参表););35声明虚函数下面是定义下面是定义CACA和和CBCB两个类,在适当的位置加上虚函数标两个类,在适当的位置加
48、上虚函数标记记virtualvirtual:class CAclass CApublic:public:void fun( )void fun( )coutcoutFun of CAn; ;Fun of CAn; ;class CB:publicclass CB:public CA CApublic:public:void fun( )void fun( ) coutcoutFun of CBn; ;Fun of CBn; ;void fb( ) coutThis is fbnvoid fb( ) coutThis is fbn; ; ;virtualvirtual36有关虚函数的说明n 类的
49、静态成员函数和内联函数不能声明为虚函数类的静态成员函数和内联函数不能声明为虚函数n 定义成员函数为虚函数,并不代表该函数是定义成员函数为虚函数,并不代表该函数是“虚虚”的,的,而是为了通过指针变量能够访问正确的方法成员,通而是为了通过指针变量能够访问正确的方法成员,通过对象名访问成员与虚函数无关过对象名访问成员与虚函数无关n 一旦在一个类中定义了虚函数,则以该类为起点的类一旦在一个类中定义了虚函数,则以该类为起点的类族中该函数都是虚函数,派生类中的相应函数不再需族中该函数都是虚函数,派生类中的相应函数不再需要用要用virtualvirtual说明说明n 构造函数不能声明为虚函数,析构函数往往声
50、明为虚构造函数不能声明为虚函数,析构函数往往声明为虚函数函数n 基类中声明虚函数时必须明确形参,派生类中相应函基类中声明虚函数时必须明确形参,派生类中相应函数不仅要同名,也要求形参相同数不仅要同名,也要求形参相同37测试class CXclass CXpublic:public: virtual int fa virtual int fa( );( ); int fb int fb( );( ); class CY:publicclass CY:public CX CXpublic:public: virtual int fb virtual int fb( );( ); class CZ:p
51、ublicclass CZ:public CY CYpublic:public: virtual int fa(int virtual int fa(int n); n); int fb(int int fb(int n); n); class CW:publicclass CW:public CZ CZpublic:public: virtual int fa virtual int fa( );( ); int fb int fb( );( ); n 说明各个类的对象能访说明各个类的对象能访问哪些函数成员?问哪些函数成员?n 哪些类的哪些函数构成哪些类的哪些函数构成一组虚函数一组虚函数 画
52、出层次结构图画出层次结构图38虚函数的实现原理 面向对象程序设计允许函数重载面向对象程序设计允许函数重载(overload)(overload)与函数与函数覆盖覆盖(override)(override),前者导致函数同名但形参不同,后者,前者导致函数同名但形参不同,后者则是同名同形参。前面已有若干示例说明后者的有关规则是同名同形参。前面已有若干示例说明后者的有关规则,核心问题是当代码中出现一个函数调用时,究竟是则,核心问题是当代码中出现一个函数调用时,究竟是调用哪一段具体的函数代码。调用哪一段具体的函数代码。 解决问题的方法是对虚函数采用动态联编方式。解决问题的方法是对虚函数采用动态联编方式
53、。动态联编动态联编也称动态绑定,是与静态联编相对而言的。也称动态绑定,是与静态联编相对而言的。当类族中含有虚函数时,为类族的每个对象安排当类族中含有虚函数时,为类族的每个对象安排虚虚函数表函数表( (是各个虚函数入口地址的列表,与数据成员是各个虚函数入口地址的列表,与数据成员安排在一起安排在一起) )。对于通过指向基类的指针访问函数成。对于通过指向基类的指针访问函数成员的情况,编译时处理成员的情况,编译时处理成“从虚函数表中找函数的从虚函数表中找函数的入口地址入口地址”。39设计图元类族的方法成员Q Q:请设计:请设计CElementCElement类的类的DrawDraw方法的功能及代码方法
54、的功能及代码CElementCElementcolor,size,bordercolor,size,borderDraw,EraseDraw,Erase,.,.CPointCPointCSegmentCSegmentstart,end,stylestart,end,styleCAreaCAreapattern,transparencypattern,transparencyCLineCLineCArcCArc. . .CCurveCCurve. . .CSquareCSquare. . .CCircleCCircle. . .CPolygonCPolygon. . .DrawDraw的功能好说
55、,就是在指定位置画出这个图元。代码的功能好说,就是在指定位置画出这个图元。代码没法写,因为在没法写,因为在CElementCElement类中并不知道图元的具体情况。类中并不知道图元的具体情况。如果真的要写代码,只能是空代码,什么也不做!如果真的要写代码,只能是空代码,什么也不做!40纯虚函数与抽象类 采用继承与派生的层次式设计构建一个类族时,采用继承与派生的层次式设计构建一个类族时,越上层的类就越抽象,越下层的类就越具体。有时上越上层的类就越抽象,越下层的类就越具体。有时上层的方法根本无法明确行为,比如层的方法根本无法明确行为,比如“图元图元”的的DrawDraw 在在C+C+中,允许一个类
56、只规定方法成员的首部而中,允许一个类只规定方法成员的首部而不编写函数体,这样的方法称为不编写函数体,这样的方法称为“纯虚函数纯虚函数”,含有,含有纯虚函数的类称为纯虚函数的类称为“抽象类抽象类”。 纯虚函数是在类声明中用如下形式定义函数成员:纯虚函数是在类声明中用如下形式定义函数成员:virtual virtual 函数值类型函数值类型 函数名函数名( (形参表形参表) )=0;=0;注意,上述格式中的注意,上述格式中的“=0”=0”表示该函数是纯虚函数。表示该函数是纯虚函数。虽然在语法上虽然在语法上virtualvirtual不是必须的,但一般只有类族不是必须的,但一般只有类族中基类的虚函数
57、才设计成不编写函数体。中基类的虚函数才设计成不编写函数体。 41抽象类的作用 抽象类中至少含有一个纯虚函数,试图调用纯虚抽象类中至少含有一个纯虚函数,试图调用纯虚函数将导致没有相应的代码可以执行,所以,函数将导致没有相应的代码可以执行,所以,不允许不允许创建抽象类的对象创建抽象类的对象! 抽象类的作用表现在规定由此向下的一个类族抽象类的作用表现在规定由此向下的一个类族( (或者类族分支或者类族分支) )中有哪些共同的方法成员,并统一这中有哪些共同的方法成员,并统一这些方法成员的函数名称及参数形式。些方法成员的函数名称及参数形式。 比如,关于比如,关于“图元图元”的类族中,的类族中,“图元图元”
58、定义方定义方法成员法成员“Draw”Draw”,“线线”定义方法成员定义方法成员“GetLengthGetLength”,区域定义方法成员,区域定义方法成员“GetAreaGetArea”,这,这些方法都无法编写代码,但在相应的类族中有相同的些方法都无法编写代码,但在相应的类族中有相同的名称,并且有相同的形参列表。名称,并且有相同的形参列表。42什么是多态多态多态(Polymorphism)(Polymorphism)简言之,简言之,“一个接口,多种实一个接口,多种实现现”l 接口,是指函数,包括普通函数和成员函数接口,是指函数,包括普通函数和成员函数l 一个接口,是指同名函数,显然不是一个函
59、数而是一一个接口,是指同名函数,显然不是一个函数而是一组函数组函数l 多种实现,是指同一个函数名之下有多种不同的代码多种实现,是指同一个函数名之下有多种不同的代码以应对不同的参数、对象、环境等因素以应对不同的参数、对象、环境等因素接口,泛指实体与外界联系的方式、通道。对象与外界接口,泛指实体与外界联系的方式、通道。对象与外界联系的方式可以有数据成员和函数成员,但联系的方式可以有数据成员和函数成员,但“封装封装”导致对于多数类而言,外界不能直接访问对象的数据导致对于多数类而言,外界不能直接访问对象的数据成员,只能通过类所提供的方法成员获取信息,或者成员,只能通过类所提供的方法成员获取信息,或者令
60、对象产生相应的行为。所以,面向对象中的令对象产生相应的行为。所以,面向对象中的“接口接口”通常指成员函数。通常指成员函数。43多态的种类有资料把多态划分为通用多态和特定多态,前者包括有资料把多态划分为通用多态和特定多态,前者包括参数多态和包含多态,后者包括重载多态和强制多参数多态和包含多态,后者包括重载多态和强制多态,态,但这样的划分值得商榷,原因见下页但这样的划分值得商榷,原因见下页1. 1. 参数多态参数多态由函数模板产生的模板函数(同由函数模板产生的模板函数(同名函数处理不同类型的数据),由类模板产生名函数处理不同类型的数据),由类模板产生的模板类,除了数据类型不同,其它部分相同的模板类,除了数据类型不同,其它部分相同2. 2. 包含多态包含多态类族中同名成员函数,在指针指类族中同名成员函数,在指针
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 三农村电商农业信息化解决方案
- 深路堑施工方案
- 隧道洞口开挖施工方案
- 股份制重组解决方案公告
- 艺术涂料防腐施工方案
- 云计算资源规划与分配手册
- 幼儿园股份合同转让协议
- 武昌医院东区施工方案
- 厂房独立基础施工方案
- 基坑监测施工方案
- 常州2025年江苏常州工程职业技术学院其他专技岗管理岗招聘笔试历年参考题库附带答案详解
- (一模)临沂市2025届高三高考第一次模拟考试政治试卷(含答案详解)
- 2025年安徽职业技术学院单招职业适应性考试题库含答案
- 2025年中考百日誓师大会校长发言稿(二)
- 1.2男生女生课件(共15张)-2024-2025学年统编版道德与法治七年级下册
- 2025年安徽审计职业学院单招职业技能测试题库附答案
- 2025年共青科技职业学院单招职业技能测试题库学生专用
- 2025年黑龙江林业职业技术学院单招职业技能测试题库含答案
- 2025年01月吉林白山市长白朝鲜族自治县事业单位公开招聘工作人员(含专项)和边境村稳边固边工作专干84人(1号)笔试历年典型考题(历年真题考点)解题思路附带答案详解
- 2025河南中烟许昌卷烟厂招聘10人易考易错模拟试题(共500题)试卷后附参考答案
- 2025年宁波市水务环境集团有限公司招聘笔试参考题库含答案解析
评论
0/150
提交评论