




已阅读5页,还剩368页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C+.NET程序设计(中),课件 设计者:杜茂康 2009年9月,第5章 继承 第6章 多态性 第7章 模板与STL 第8章 异常,继承使软件复用变得简单、易行,可以通过继承复用已有的程序资源,缩短软件开发的周期。 本章主要介绍继承的方式,要注意在继承方式下派生类与基类对象之间的关系,以及派生类构造函数如何提供对基类的构造。,5.1 继承的概念,1、继承的概念,以存在的类为基础定义新的类,新类即捅有基类的数据成员和成员函数。,5.1 继承的概念,2、继承目的 代码重用code resue 描述能力:类属关系广泛存在 IsA vs. HasA 3、有关概念 基类,超类 派生类,子类,5.1 继承的概念,4、派生类可实施的对基类的改变 增加新的数据成员和成员函数。 重载基类的成员函数。 重定义基类已有的成员函数。 改变基类成员在派生类中的访问属性。 5、派生类不能继承基类的以下内容 基类的构造函数和析构函数。 基类的友元函数。 静态数据成员和静态成员函数,5.1 继承的概念,5.2 继承方式,C+的继承类型 可分为公有继承、保护继承和私有继承,也称为公有派生、保护派生和私有派生。 不同继承方式会不同程度地影响基类成员在派生类中的访问权限。,5.2.1 继承方式,继承语法形式 class B ; class D : private | protected | public B ;,public 最常见的派生方式 维持基类成员的可访问性 派生类不可直接访问基类的private成员,可通过基类的共有成员函数访问,5.2.2 公有继承,例题ch5_1.cpp,class base int x; public: void setx(int n) x=n; int getx() return x; void showx() coutxendl; ;,class derived:public base int y; public: void sety(int n) y=n; void sety() y=getx(); void showy() coutyendl; ;,Setx() Getx() Showx(),x,Setx() Getx() Showx(),x,Sety() Gety() Showy(),y,接口,私有数据,base,derived,void main() derived obj; obj.setx(10); obj.sety(20); obj.showx(); obj.showy(); obj.sety(); obj.showx(); obj.showy(); ,5.2.2 公有继承,5.2.3 私有继承,Private 基类的中的public成员在派生类中是private, private成员在派生类中不可访问。 【例5】 私有继承的例子 #include using namespace std; class Base int x; public: void setx(int n)x=n; int getx()return x; void showx()coutxendl; ;,class derived:private base int y; public: void sety(int n)y=n; void sety() y=getx(); void showy() coutyendl; ;,void main() derived obj; obj.setx(10);/cannot access obj.sety(20); obj.showx();/cannot access obj.showy(); ,Setx() Getx() Showx(),x,Setx() Getx() Showx(),x,Sety() Gety() Showy(),y,接口,私有数据,base,derived,基类中protected的成员 类内部:可以访问 类的使用者:不能访问 类的派生类成员:可以访问 【例5-2】 保护成员的例子,5.2.4 保护成员,class B private: int i; protected: int j; public: int k; ; class D: public B public: void f() i=1;/cannot access j=2; k=3; ; void main() B b; b.i =1;/cannot access b.j=2; /cannot access b.k=3; ,K,k,i,i,f(),接口,私有数据,B,D,j,j,5.2.5 保护继承,派生方式为protected的继承称为保护继承,在这种继承方式下,基类的public成员在派生类中会变成protected成员,基类的protected和private成员在派生类中保持原来的访问权限。 【例】 保护继承的例子。 #include using namespace std; class Base int x; protected: int getx() return x; public: void setx(int n) x=n; void showx() coutxendl; ;,class Derived:protected Base int y; public: void sety(int n) y=n; void sety() y=getx(); /访问基类的保护成员 void showy() coutyendl; ; void main() Derived obj; obj.setx(10); /错误 obj.sety(20); obj.showx(); /错误, obj.showy(); ,基类成员在派生类中的访问权限,不能继承的基类内容 1.构造函数、析构函数 2.友员关系 3.针对基类定义的一些特殊运算符,如new等。,派生类,5.3 基类与派生类的关系,1、成员函数的重定义和名字隐藏 派生类对基类成员函数的重定义或重载会影响基类成员函数在派生类中的可见性,基类的同名成员函数会被派生类重载的同名函数所隐藏。 【例5-3】 派生类重载基类成员函数的例子。,2、派生类和基类的关系 派生类继承了基类的所有成员,尽管有些继承的成员是不可访问的 派生类可以: 添加新的数据成员和/或函数成员 修改继承的函数成员的行为(overloading) 覆盖继承的函数成员(overriding),class Base base members; class Derived: Base new members;,派生类对基类成员的访问形式 通过派生类对象直接访问基类成员 在派生类成员函数中直接访问基类成员 通过基类名字限定访问被重载的基类成员名,5.3 基类与派生类的关系,5.3 基类与派生类的关系,【例5-3】 在派生类中重载基类成员。 /CH5-3.cpp #include using namespace std; class Base int x; public: void setx(int i) x=i; void set(int n) x=n; void print() cout“Base class: x=“xendl; ;,public: void set(int p,int k) m=p; n=k; /重载基类的成员函数set() void set(int i,int j,int k) /重载成员函数 set() Base:set(i); /调用基类成员函数 set() m=j; n=k; void print() /重定义基类的成员函数print() Base:print(); cout“Derived Class: m=“mendl; cout“Derived Class: n=“nendl; ;,void main() Derived d; d.set(1,3); /L1 d.print(); /L2 d.set(5,6,7); /L3 d.print(); / d.set(10); /L4,错误,只能是d.Base:set(10) d.Base:print(); /L5 d.setx(8); /L6 ,5.3.2 访问基类成员,【例5-4】 在派生类中访问基类成员。 /CH5-4.cpp #include using namespace std; class B int x; public: void set(int i) x=i; void setx(int i) x=i; void printx() cout“x=“xendl; ;,class D:public B int y; public: void sety(int p)y=p; void printy() cout“y=“yendl; void setxy(int i,int j) setx(i); /L1 在派生类成员函数中直接访问基类成员 y=j; void set(int i,int j) B:set(i); /L2 访问基类set成员 y=j; ; void main() D obj; obj.setx(2); /L3 访问基类成员 obj.printx(); /L4 访问基类成员, 输出 x=2 obj.sety(3); /L5 访问派生类成员 obj.set(1,2); /L6 访问派生类set成员 obj.B:set(3); /L7 访问基类set成员 ,B和D的示意类图,5.4 构造函数和析构函数,补充:类对象成员的构造 先构造对象成员 再构造自身(调用构造函数) 例题ch.cpp,class A public: A() cout“Constructing A“endl; A() cout“Destructing A“endl; ; class B public: B() cout“Constructing B“endl; B() cout“Destructing B“endl; ;,class C public: C() cout“Constructing C“endl; C() cout“Destructing C“endl; B b; A a; ; void main() C c; ,Constructing B Constructing A Constructing C Destructing C Destructing A Destructing B,如果: class B:public A class C:public B 结果又当如何?,5.4.1 派生类构造函数的定义,派生类可能有多个基类,也可能包括多个成员对象,在创建派生类对象时,派生类的构造函数除了要负责本类成员的初始化外,还要调用基类和成员对象的构造函数,并向它们传递参数,以完成基类子对象和成员对象的建立和初始化。 派生类只能采用构造函数初始化列表的方式向基类或成员对象的构造函数传递参数,形式如下: 派生类构造函数名(参数表):基类构造函数名(参数表),成员对象名1(参数表), / ,5.4.1 派生类构造函数的定义,【例5-5】 派生类Derived以构造函数初始化列表的方式向基类构造函数提供参数。 /CH5-5.cpp #include using namespace std; class Base private: int x; public: Base(int a) x=a; cout“Base constructor x=“xendl; Base() cout“Base destructor.“endl; ;,class Derived:public Base private: int y; public: Derived(int a,int b):Base(a) /派生类构造函数的初始化列表 y=b; cout“Derived constructor y=“yendl; Derived() cout“Derived destructor.“endl; ; void main() Derived d(1,2); ,派生类对象的构造 先构造基类 再构造成员 最后构造自身(调用构造函数) 基类构造顺序由派生层次决定:最远的基类最先构造 成员构造顺序和定义顺序符合 析构函数的析构顺序与构造相反,5.4.2 构造函数和析构函数调用次序,例题ch.cpp,class A public: A() cout“Constructing A“endl; A() cout“Destructing A“endl; ; class B public: B() cout“Constructing B“endl; B() cout“Destructing B“endl; ;,class C public: C() cout“Constructing C“endl; C() cout“Destructing C“endl; ;,class D:public C public: D() cout“Constructing D“endl; D() cout“Destructing D“endl; B b; A a; C c; ; void main() D d; ,Constructing C Constructing B Constructing A Constructing C Constructing D Destructing D Destructing C Destructing A Destructing B Destructing C,5.4.3 构造函数和析构函数的构造规则,1、派生类可以不定义构造函数的情况 当具有下述情况之一时,派生类可以不定义构造函数。 基类没有定义任何构造函数。 基类具有缺省参数的构造函数。 基类具有无参构造函数。,【例5-7】 没有构造函数的派生类。 /CH5-7.cpp #include using namespace std; class A public: A() cout“Constructing A“endl; A() cout“Destructing A“endl; ; class B:public A public: B() cout“Destructing B“endl; ; void main() B b; ,5.4.3 构造函数和析构函数的构造规则,2、派生类必须定义构造函数的情况 当基类或成员对象所属类只含有带参数的构造函数时,即使派生类本身没有数据成员要初始化,它也必须定义构造函数,并以构造函数初始化列表的方式向基类和成员对象的构造函数传递参数,以实现基类子对象和成员对象的初始化。,4.4.3 构造函数和析构函数的构造规则,【例5-8】 派生类构造函数的定义。 /CH5-8.cpp #include using namespace std; class Point protected: int x,y; public: Point(int a,int b=0) x=a; y=b; cout“constructing point(“x“,“y“)“endl; ;,class Line:public Point protected: int len; public: Line(int a,int b,int l):Point(a,b) /构造函数初始化列表 len=l; cout“Constructing Line,len .“lenendl; ; void main() Line L1(1,2,3); ,3、派生类的构造函数只负责直接基类的初始化 C+语言标准有一条规则:如果派生类的基类同时也是另外一个类的派生类,则每个派生类只负责它的直接基类的构造函数调用。 这条规则表明当派生类的直接基类只有带参数的构造函数,但没有默认构造函数时(包括缺省参数和无参构造函数),它必须在构造函数的初始化列表中调用其直接基类的构造函数,并向基类的构造函数传递参数,以实现派生类对象中的基类子对象的初始化。 这条规则有一个例外情况,当派生类存在虚基类时,所有虚基类都由最后的派生类负责初始化。,5.4.3 构造函数和析构函数的构造规则,【例5-9】 当同时存在直接基类和间接基类时,每个派生类只负责其直接基类的构造。 /CH5-9.cpp #include using namespace std; class A int x; public: A(int aa) x=aa; cout“Constructing A“endl; A() cout“Destructing A“endl; ;,class B:public A public: B(int x):A(x) cout“Constructing B“endl; ; class C :public B public: C(int y):B(y) cout“Constructing C“endl; ; void main() C c(1); ,4、构造函数的调用时间和次序 当派生类具有多个基类和多个对象成员,它们的构造函数将在创建派生类对象时被调用,调用次序如下: 基类构造函数对象成员构造函数派生类构造函数,5.4.3 构造函数和析构函数的构造规则,(1)当有多个基类时,将按照它们在继承方式中的声明次序调用,与它们在构造函数初始化列表中的次序无关。当基类A本身又是另一个类B的派生类时,则先调用基类B的构造函数,再调用基类A的构造函数。 (2)当有多个对象成员时,将按它们在派生类中的声明次序调用,与它们在构造函数初始化列表中的次序无关。 (3)当构造函数初始化列表中的基类和对象成员的构造函数调用完成之后,才执行派生类构造函数体中的程序代码。,5.4.3 构造函数和析构函数的构造规则,【例5-10】 构造函数的调用次序验证。 /CH5-10.cpp #include using namespace std; class A int x; public: A(int i=0) x=i; cout “A-“xendl; A()cout“Des-A-“xendl; ;,class B int y; public: B(int i) y=i; cout “B-“yendl; B()cout“Des-B-“yendl; ; class C int z; public: C(int i) z=i; cout “C-“zendl; C()cout“Des-C-“zendl; ;,class D : public B public: C c1, c2; A a0,a4; D( ) : a4(4),c2(2),c1(1),B(1) cout “D-5“endl; D()cout“Des-D-“5endl; ; void main() D d; ,5.5 多重继承,5.5.1 多继承的概念和应用 C+允许一个类从一个或多个基类派生。如果一个类只有一个基类,就称为单一继承。如果一个类具有两个或两个以上的基类,就称为多重继承。多继承的形式如下: class 派生类名:继承方式 基类名1,继承方式 基类名2, ; 其中,继承方式可以是public、protected、private,5.5.1 多继承的概念和应用,【例5-11】 上图的简单程序。 /CH5-11.cpp #include using namespace std; class Base1 private: int x; protected: int getx() return x; public: void setx(int a=1) x=a; ;,5.5.1 多继承的概念和应用,class Base2 private: int y; public: void sety(int a) y=a; int gety() return y; ; class Base3 private: int z; public: void setz(int a) z=a; int getz() return z; ;,class Derived:public Base1,public Base2,public Base3 private: int d; public: void setd(int a) d=a; void display(); ; void Derived:display() cout“Base1x=“getx()endl; cout“Base2y=“gety()endl; cout“Base3z=“getz()endl; cout“Derivedd=“dendl; void main() Derived obj; obj.setx(1); obj.sety(2); obj.setz(3); obj.setd(4); obj.display(); ,5.5.2 多继承下的二义性,在多继承方式下,派生类继承了多个基类的成员,当两个不同基类拥有同名成员时,容易产生名字冲突问题。 【例5-12】 类A和类B是MI的基类,它们都有一个成员函数f,在类MI中就有通过继承而来的两个同名成员函数f。,/CH5-12.cpp #include using namespace std; class A public: voidf() cout“From A“endl; ; class B public: void f() cout“From B“endl; ; class MI: public A, public B public: void g() cout“From MI“endl; ; void main() MI mi; mi.f(); /错误 mi.A:f(); /正确 ,5.5.3 多继承的构造函数与析构函数,派生类必须负责为每个基类的构造函数提供初始化参数,构造的方法和原则与单继承相同。 构造函数的调用次序仍然是先基类,再对象成员,然后才是派生类的构造函数。 基类构造函数的调用次序与它们在被继承时的声明次序相同,与它们在派生类构造函数的初始化列表中的次序没有关系。 多继承方式下的析构函数调用次序仍然与构造函数的调用次序相反。,【例5-13】 类Base1、Base2、Base3、Derived的继承关系如图所示,验证其构造函数和析构函数的调用次序。,5.5.3 多继承的构造函数与析构函数,5.6.1 引入的原因重复基类 派生类间接继承同一基类使得间接基类(Person)在派生类中有多份拷贝,引发二义性。,5.6 继拟继承,虚拟基类在派生类中只存在一份拷贝,解决了基类数据成员的二义性问题,5.6.1 引入的原因重复基类,1、虚拟继承virtual inheritance的定义 语法 class derived_class : virtual base_class 虚基类virtual base class 被虚拟继承的基类 在其所以的派生类中,仅出现一次,5.6.2 虚拟继承的实现,class A public: void vf() cout“I come from class A“endl; ; class B: public A; class C: public A; class D: public B, public C; void main() D d; d.vf (); / error ,A,B,C,D,Vf(),Vf(),Vf(),B.A:Vf() C.A:Vf(),A,Vf(),【例5-14】 类A是类B、C的虚基类,类D从类B、C继承,在类D中调用基类A的成员会产生二义性。,class A public: void vf() cout“I come from class A“endl; ; class B: virtual public A; class C: virtual public A; class D: public B, public C; void main() D d; d.vf(); / okay ,A,B,C,D,Vf(),Vf(),Vf(),A:Vf(),将【例5-14】 改为虚拟继承不会产生二义性。,2、虚拟继承的构造次序 虚基类的初始化与一般的多重继承的初始化在语法上是一样的,但构造函数的调用顺序不同; 若基类由虚基类派生而来,则派生类必须提供对间接基类的构造(即在构造函数初始列表中构造虚基类,无论此虚基类是直接还是间接基类) 调用顺序的规定: 先调用虚基类的构造函数,再调用非虚基类的构造函数 若同一层次中包含多个虚基类,这些虚基类的构造函数按它们的说明的次序调用 若虚基类由非基类派生而来,则仍然先调用基类构造函数,再调用派生类构造函数,【例5-15】 虚基类的执行次序分析。 /CH5-15.cpp #include using namespace std; class A int a; public: A() cout“Constructing A“endl; ; class B public: B() cout“Constructing B“endl; ;,5.6.2 虚拟继承的实现,class B1:virtual public B ,virtual public A public: B1(int i) cout“Constructing B1“endl; ; class B2:public A,virtual public B public: B2(int j) cout“Constructing B2“endl; ; class D: public B1, public B2 public: D(int m,int n): B1(m),B2(n) cout“Constructing D“endl; A a; ; void main() D d(1,2); ,3、虚基类由最终派生类初始化 在没有虚拟继承的情况下,每个派生类的构造函数只负责其直接基类的初始化。但在虚拟继承方式下,虚基类则由最终派生类的构造函数负责初始化。 在虚拟继承方式下,若最终派生类的构造函数没有明确调用虚基类的构造函数,编译器就会尝试调用虚基类不需要参数的构造函数(包括缺省、无参和缺省参数的构造函数),如果没找到就会产生编译错误。,4.6.2 虚拟继承的实现,【例5-16】 类A是类B、C的虚基类,类ABC从B、C派生,是继承结构中的最终派生类,它负责虚基类A的初始化。 /CH5-16.cpp #include class A int a; public: A(int x) a=x; cout“Virtual Bass A.“endl; ;,class B:virtual public A public: B(int i):A(i) cout“Virtual Bass B.“endl; ; class C:virtual public A int x; public: C(int i):A(i) cout“Constructing C.“endl; x=i; ; class ABC:public C, public B public: ABC(int i,int j,int k):C(i),B(j),A(i) /L1,这里必须对A进行初始化 cout“Constructing ABC.“endl; ; void main() ABC obj(1,2,3); ,5.7基类与派生类对象的关系,基类对象与派生类对象之间存在赋值相容性。包括以下几种情况: 把派生类对象赋值给基类对象。 把派生类对象的地址赋值给基类指针。 用派生类对象初始化基类对象的引用。 反之则不行,即不能把基类对象赋值给派生类对象;不能把基类对象的地址赋值给派生类对象的指针;也不能把基类对象作为派生对象的引用。,【例5-17】 把派生类对象赋值给基类对象的例子。 /CH5-17.cpp #include using namespace std; class A int a; public: void setA(int x) a=x; int getA() return a; ; class B:public A int b; public: void setB(int x) b=x; int getB() return b; ;,void f1(A a, int x) a.setA(x); void f2(A *pA, int x) pA-setA(x); void f3(A ,pA-setA(20); coutgetA()getA()endl; coutb1.getA()endl; b1.setA(7); coutb1.getA()endl; f1(b1,100); coutb1.getA()endl; f2( ,5.7基类与派生类对象的关系,说明: 不论以哪种方式把派生类对象赋值给基类对象,都只能访问到派生类对象中的基类子对象部份的成员,不能访问派生类的自定义成员。 只能把派生类对象赋值给基类对象,不能把基类对象赋值给派生类对象。,5.7基类与派生类对象的关系,5.8继承与组合,继承与组合(也称合成)是C+实现代码重用的两种主要方法。通过继承,派生类可以获得基类的程序代码,从而达到代码重用的目的。而组合则体现了类之间的另一种关系,是指一个类可以包容另外的类,即用其他类来定义它的对象成员。 继承关系常被称为“Is-a”关系,即两个类之间若存在Is-a关系,就可以用继承来实现它。比如,水果和梨,水果和苹果,它们就具有Is-a关系。因为梨是水果,苹果也是水果,所以梨和苹果都可以从水果继承,获得所有水果都具有的通用特征。 组合常用于描述类之间的“Has-a”关系,即一个类拥有另外一些类。比如,图书馆有图书,汽车有发动机、车轮胎、座位等,计算机有CPU、存储器、显示器等,这些都可以用类的组合关系来实现。,5.8 继承与组合,【例5-18】设计一个成绩单管理的程序。其中包括学生信息,如姓名、身高、性别、学号、专业等,学生学习某门课程后将得到一个成绩,相关的信息有课程名称、课程编号学分和成绩等。,5.8 继承与组合,经过抽象与继承,构造出图所示的类结构,5.8 继承与组合,将每个类的声明与实现分别独立保存在与类同名的.h头文件和.cpp文件中。 Course类,5.8 继承与组合,Student类 项目文件,The End,谢谢大家!,第6章 多态性,多态性是面向对象程序设计语言的又一重要特征,它是指不同对象接收到同一消息时会产生不同的行为。继承所处理的是类与类之间的层次关系问题,而多态则是处理类的层次结构之间,以及同一个类内部同名函数的关系问题。简单地说,多态就是在同一个类或继承体系结构的基类与派生类中,用同名函数来实现各种不同的功能。,6.1 静态绑定和动态绑定,1、多态polymorphism 对象根据所接收的消息而做出动作,同样的消息为不同的对象接收时可导致完全不同的行动,该现象称为多态性。 简单的说:单接口,多实现 2、联编 一个程序常常会调用到来自于不同文件或C+库中的资源(如函数、对话框)等,需要经过编译、连接才能形成为可执行文件,在这个过程中要把调用函数名与对应函数(这些函数可能来源于不同的文件或库)关联在一起,这个过程就是绑定(binding),又称联编。,6.1 静态绑定和动态绑定,3、静态绑定与静态绑定 静态绑定又称静态联编,是指在编译程序时就根据调用函数提供的信息,把它所对应的具体函数确定下来,即在编译时就把调用函数名与具体函数绑定在一起。 动态绑定又称动态联编,是指在编译程序时还不能确定函数调用所对应的具体函数,只有在程序运行过程中才能够确定函数调用所对应的具体函数,即在程序运行时才把调用函数名与具体函数绑定在一起。,4、多态性的实现 编译时多态性: -静态联编(连接)-系统在编译时就决定如何实现某一动作,即对某一消息如何处理.静态联编具有执行速度快的优点.在C+中的编译时多态性是通过函数重载和运算符重载实现的。 运行时多态性: -动态联编(连接)-系统在运行时动态实现某一动作,即对某一消息在运行过程实现其如何响应.动态联编为系统提供了灵活和高度问题抽象的优点,在C+中的运行时多态性是通过继承和虚函数实现的。,6.1 静态绑定和动态绑定,6.2 函数重载,1、函数重载的意义 函数重载意义在于用户可以通过同一个函数名访问某一类或一组相关操作的函数,由编译器决定具体的函数调用,有助于复杂问题的减化。 2、函数重载的类型 函数重载可分为普通函数重载、类成员函数重载以及继承结构中基类和派生类成员函数的重载几种情况。 3、函数重载的要求 要求重载函数具有不同的函数原型,即重载函数要在参数类型、参数个数或参数顺序上有所区别,不能有同一作用域内的两个同名函数具有完全相同的参数表。,【例6-1】重载复数的加法,实现复数实部与普通数据相加,两个复数相加的运算。 #include using std:cout; using std:endl; class Complex double real,imag; public: Complex(double r=0,double i=0):real(r),imag(i) Complex Add(double x) retur n Complex(x+real,imag); Complex Add(Complex c) return Complex(real+c.real,imag+c.imag); void display()coutreal“ + “imag“i“endl; double getReal()return real; double getImag()return imag; ;,Complex Add(double r,Complex c) return(r+c.getReal(),c.getImag (); Complex Add(Complex c1,Complex c2) return Complex(c1.getReal()+c2.getReal(),c1.getImag()+c2.getImag(); void main() Complex c1(2,3),c2(5,6),c3; c3=c1.Add(10); c3.display(); c3=c1.Add(c2); c3.display(); c3=Add(c1,c2); c3.display(); Add(Complex,Complex) ,6.3 运算符重载,运算符重载是C+的一项强大功能。通过重载,可以扩展C+运算符的功能,使它们能够操作用户自定义的数据类型,增加程序代码的直观性和可读性。 本章主要介绍 类成员运算符重载与友元运算符重载, 二元运算符与一元运算符重载, 运算符+、-、()重载, this指针与运算符重载及 流运算符的重载,6.3.1 运算符重载的概念,1、运算符重载的概念 C+的运算符对语言预定义类型是重载的 int i=2+3; double j=2+4.8; float f=float(3.1)+float(2.0); 对于上面的3个加法表达式,C+系统提供了类似于下面形式的运算符重载函数: int operator+(int,int); double operator+(int,double); float operator+(float,float);,6.3.1 运算符重载的概念,C+允许程序员通过重载扩展运算符的功能,使重载后的运算符能够对用户自定义的数据类型进行运算。 比如,设有复数类Complex,其形式如下: class Complex double real,image; public: ; 假设定义了下面的复数对象,并且要实现两个复数相加的运算。 Complex c1,c2,c3; c1=c2+c3;,why? 使程序便于编写和阅读 使程序定义类型与语言内建类型更一致 how? 使用特殊的成员函数 使用自由函数,一般为友元,6.3.1 运算符重载的概念,6.3.2 运算符重载限制 可以重新定义大多数运算符, + - / * % & | ! = += -= *= /= %= = &= |= = = () new new delete delete 不能定义新的运算符 不能重载某些特殊运算符,包括: . .* - : ?: sizeof typeid 不能改变运算符的目、优先级、结合性 不能重载语言预定义类型的运算符含义 如 # # 无隐含重载,即:定义了+,并隐含不定义+= 程序定义的含义与运算符固有含义吻合,6.3.3 类外运算符重载,运算符可以非类成员的普通形式重载,运算符的计算结果是值,因此运算符函数是要返回值的函数。其重载的语法形式如下: 返回类型 operator(参数表) 其中,operator是C+的保留关键字,表示运算符函数。代表要重载的运算符,它可以是前面列举的可重载运算符中的任何一个。,【例6-2】重载“+”运算符的功能,实现两个复数相加的运算。 /CH6-2.cpp #include using std:cout; using std:endl; struct Complex double real,imag; Complex(double r=0,double i=0):real(r),imag(i) void display()coutreal“ + “imag“i“endl; ;,Complex operator+(Complex c1,Complex c2) Complex t; t.real =c1.real +c2.real ; t.imag =c1.imag +c2.imag ; return t; void main() Complex c1(2,3),c2(5,6),c3; c3=c1+c2; c3.display(); /输出:7+9i ,6.4 类运算符的重载,1、C+为类默认的重载运算符 赋值运算(=); 取类对象地址的运算函符(&); 成员访问运算(如“.”和“-”)。 这些运算符不需要重载就可以使用,但要在类中使用其他运算符,就必须明确地重载它们。,6.4.1 类成员运算符重载,(1)非静态成员运算符重载 以类成员形式重载的运算符参数比实际参数少一个,第1个参数是以this指针隐式传递的。 class Complex double real,image; public: Complex operator+(Complex b) ;,6.4.1 类运算符的重载,(2). 友元运算符重载 如果将运算符函数作为类的友元重载,它需要的参数个数就与运算符实际需要的参数个数相同。比如,若用友元函数重载Complex类的加法运算符,则形式如下: class Complex friend Complex operator+(Complex a,Complex b); /声明 / ; Complex operator+(Complex a,Complex b) /定义, 重载二元运算符,1、二元运算符的调用形式与解析 aabb 可解释成 aa.operator(bb) 或解释成 operator(aa,bb) 如果两者都有定义,就按照重载解析 class X public: void operator+(int); X(int); ; void operator+(X,X); void operator+(X,double);,void f(X a) a+2; /a.operator+(2) 2+a; /:operator+(X(2),a) a+2.0; /:operator+(X,double); , 重载二元运算符,1 作为成员函数重载 作为类的非静态成员函数的二元运算符,只能够有一个参数,这个参数是运算符右边的参数,它的第一个参数是通过this指针传递的,其重载形式类似于下: class X T1 operator(T2 b) ; 其中,T1是运算符函数的返回类型,T2是参数的类型,原则上T1、T2可以是任何数据类型,但事实上它们常与X相同。, 重载二元运算符,【例6-3】 有复数类Complex,利用运算符重载实现复数的加、减、乘、除等复数运算。 /CH6-3.cpp #include using namespace std; class Complex private: double r, i; public: Complex (double R=0, double I=0):r(R), i(I) ; Complex operator+(Complex b); Complex operator-(Complex b); Complex operator*(Complex b); Complex operator/(Complex b); void disp
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 高温天气下混凝土施工技术管理方案
- 精益求精无财产无子女离婚协议书起草与执行
- 风光制氢醇一体化项目环境影响报告书
- 离婚协议翻译及国际婚姻法律事务代理服务合同
- 男方离婚协议书:财产分配、子女抚养及共同债务分担
- 深远海养殖设备选型与安装方案
- 精细化工中试基地建设项目建设工程方案
- 城市更新安全保障与应急管理方案
- 分布式新能源市场2025年技术创新与新能源发电研究报告
- 2025年新型储能材料在微电网中的应用前景报告
- 室外消防钢丝网骨架塑料复合PE管施工及方案
- 带秋字的古诗飞花令
- 体育原理完整版
- 超声引导下坐骨神经阻滞
- 【上课用】 高三数学一轮复习-错位相减法课件
- 医院医院质量与安全管理委员会章程
- 小学二年级上册语文全册课件
- 《放飞烦恼-拥抱快乐-》-心理健康p课件
- 隧道施工安全教育培训
- GB 20052-2020 电力变压器能效限定值及能效等级
- 道路运输企业风险辨识风险分级管控清单
评论
0/150
提交评论