VC++ 课件 第02章 C++面向对象.ppt_第1页
VC++ 课件 第02章 C++面向对象.ppt_第2页
VC++ 课件 第02章 C++面向对象.ppt_第3页
VC++ 课件 第02章 C++面向对象.ppt_第4页
VC++ 课件 第02章 C++面向对象.ppt_第5页
已阅读5页,还剩53页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第2章C+面向对象程序设计,2.1类和对象,2.1.1类的定义 定义类的一般格式如下: class private: protected: public: ; 类中的数据和函数是类的成员,分别称为数据成员和成员函数。 public类成员,是公有的,能被外面的程序访问; private类成员,是私有的,只能由类中的函数所使用,而不能被外面的程序所访问。 protected类成员,是私有的,只能由类中的函数及其派生类所使用,而不能被外面的程序所访问。 是类定义中的实现部分,这部分包含所有在类体中声明的函数的定义。 当类的成员函数的函数体在类的外部定义时,必须由作用域运算符“:”来通知编译系统该函数

2、所属的类。,2.1类和对象,定义类时应注意: (1) 不允许对所定义的数据成员进行初始化,例如类CMeter中,下面的定义是错误的: class CMeter . private: int m_nPos = 10; . ; (2)在“public:”或“private:”后面定义的所有成员都是公有或私有的,直到下一个“public:”或“private:”出现为止。 (3) 关键字public和private可以在类中出现多次,且前后的顺序没有关系; (4) 关键字protected(保护)也可修饰成员的类型,它与private两者基本相似,但在类的继承时有所不同。 (5)数据成员的类型可以是

3、整型、浮点型、字符型、数组、指针和引用等,也可以是另一个类的对象。,2.1类和对象,2.1.2 对象的定义 定义对象,格式: 定义的对象既可是一个普通对象,也可是一个数组对象或指针对象。 对象的成员就是该对象的类所定义的成员。对象成员有数据成员和成员函数,其表示方式如下: . .() 前者用来表示数据成员,后者用来表示成员函数。“.”是一个运算符,用来表示对象的成员。 指向对象的指针的成员表示如下: - -() “-”与“.”运算符的区别是:“-”用来表示指向对象的指针成员,“.”用来表示一般对象成员。前者表示数据成员,后者表示成员函数。 下面的两种表示是等价的: - (*). 这对于成员函数

4、也适用。另外,引用对象的成员表示与一般对象的成员表示相同。,2.1类和对象,2.1.2几个C+程序 在屏幕上输出一个由星号形成的三角形。 / 输出星号的三角形阵列 #include void DoDraw(int num); / 声明一个全局函数 void main() int num=5; / 定义并初始化变量 DoDraw(num);/ 函数的调用 void DoDraw(int num)/ 函数的定义 for (int i=0; inum; i+)/ 循环语句 for (int j=0; j=i; j+) cout*; coutn; ,2.1类和对象,用类的概念重写例Ex_Draw。 /

5、 输出星号的三角形阵列 #include class CDrawArray/ 定义一个类 public: void DoDraw(int num);/ 声明类的公有成员函数 ; void CDrawArray:DoDraw(int num)/ 成员函数的实现 for (int i=0;inum;i+) for (int j=0;j=i;j+) cout*; coutn; void main() int num=5; CDrawArray myDraw;/ 定义类的一个对象 myDraw.DoDraw(num);/ 调用此对象的成员函数 ,2.1类和对象,2.1.3构造函数和析构函数 构造函数

6、为了能给数据成员自动设置某些初始值,就要使用类的特殊成员函数构造函数。构造函数的最大特点是在对象建立时它会被自动执行,用于变量、对象的初始化代码一般放在构造函数中。 构造函数必须与相应的类同名,可以带参数,也可以不带参数,可以重载。例如: class CMeter public: CMeter(int nPos = 10)/ 构造函数 m_nPos = nPos; . 构造函数CMeter(int nPos=10)中,nPos被设置了10,构造函数中相应实参没有被指定时,使用此缺省值。由于构造函数的参数只能在定义对象时指定。 CMeter oMeter;和CMeter oMeter(10);,

7、2.1类和对象,析构函数 与构造函数相对应的是析构函数。析构函数是另一种特殊的C+成员函数,它只是在类名称前面加上一个“”符号。每一个类只有一个析构函数,没有任何参数,也不返回任何值。例如: class CMeter public: . CMeter( ) / 析构函数 . 析构函数一般在下列两种情况下被自动调用: (1) 当对象定义在一个函数体中,函数调用结束后,析构函数被自动调用。 (2) new为对象分配动态内存,用delete释放对象时,析构函数被自动调用。,2.1类和对象,默认构造函数和析构函数 类定义时,如果没有定义任何构造函数,编译器自动生成一个不带参数的默认构造函数,格式如下:

8、 :() 按构造函数的规定,默认构造函数名同类名。默认构造函数的这样格式也可由用户定义在类体中。在程序中定义一个对象而没有指明初始化时,则编译器便按默认构造函数来初始化该对象。 默认构造函数对对象初始化时,则将对象的所有数据成员都初始化为零或空。 如果一个类中没有定义析构函数,编译系统生成一个默认析构函数,格式: :() 默认析构函数名与该类的类名同名。是一个空函数。,2.1类和对象,2.1.4类的对象成员 在实际应用中往往需要多个类,这时就可能把一个已定义类的对象作为另一个类的成员。为了能对这些对象成员进行初始化,构造函数定义格式: :(形参表):对象1(参数表), 对象2(参数表), ,

9、对象n(参数表) 对象1、对象2、对象n就是该类使用的其他类的对象,冒号“:”后面的列表称为成员初始化列表。 说明: (1)对象成员必须初始化,但不能将对象成员直接在构造函数体内进行初始化。 (2)对象成员初始化时,必须有相应的构造函数,且多个对象成员的构造次序不是按初始化成员列表的顺序,而是按各类声明的先后次序进行的。 (3)对象成员初始化也可在类的外部进行,但必须与构造函数在一起。 (4)事实上,成员初始化列表也可用于类中的普通数据成员的初始化。,2.1类和对象,2.1.5常类型 常对象 常对象是指对象常量,定义格式: const 定义常对象要进行初始化,该对象不能再被更新,修饰符cons

10、t可以放在类名后面,也可以放在类名前面。 常对象成员分为常成员函数和常数据成员。,2.1类和对象,常成员函数可以理解成是“只读”函数,既不能更改数据成员的值,也不能调用那些引起数据成员值变化的成员函数,只能调用const成员函数。例如: class CDate public: CDate( int mn, int dy, int yr );/ 构造函数 int getMonth() const; / 常成员函数 void setMonth( int mn ); / 一般成员函数 int month;/ 数据成员 ; int CDate:getMonth() const return month

11、; / 不能修改数据成员的值,只有一个返回值 void CDate:setMonth( int mn ) month = mn;/ 可以使用赋值等语句,修改数据成员的值 ,2.1类和对象,常数据成员 类型修饰符const不仅可以说明成员函数,也可以说明数据成员。类中声明const数据成员时,只能通过成员初始化列表的方式来生成构造函数对数据成员初始化。例如: 例Ex_ConstData 常数据成员的使用。 #include class COne public: COne(int a):x(a),r(x)/ 常数据成员的初始化 void print(); const int 该程序的运行结果为:x

12、 = 100, y = 10, r = 100,2.1类和对象,2.1.9 类的作用域和对象的生存期 1. 类的作用域 类的作用域是指在类的定义中由一对花括号所括起来的部分。 具体地讲,某个类A中某个成员M在下列情况下具有类A的作用域: (1) 成员M出现在类A的某个成员函数中,并且该成员函数没有定义同名标识符。 (2) 成员M出现在a.M或A:M表达式中,其中a是A的对象。 (3) 成员M出现在pa-M这样的表达式中,其中pa是一个指向A类对象的指针。,不同存储的对象生存期不同。所谓对象的生存期是指对象从被创建开始到被释放为止的时间。按生存期的不同对象分为三种: (1)局部对象:对象被定义时

13、调用构造函数,该对象被创建,当程序退出定义该对象所在的函数体或程序块时,调用析构函数,释放该对象。 (2)静态对象:当程序第一次执行所定义的静态对象时,该对象被创建,当程序结束时,该对象被释放。 (3)全局对象:当程序开始时,调用构造函数创建该对象,当程序结束时调用析构函数释放该对象。 局部对象被定义在函数体或程序块内,作用域小,生存期短。静态对象被定义在文件中,作用域比较大,生存期也比较大。全局对象被定义在某个文件中,作用域在包含该文件的整个程序中,作用域是最大的,生存期也是最长的。,2.1类和对象,2. 对象的生存期,2.2继承和派生类,2.2.1继承 继承(inheritance)就是利

14、用已有的数据类型定义出新的数据类型。 在继承关系中,被继承的类称为基类(base class),而通过继承关系定义出来的新类则被称为派生类(derived class)。 一个派生类既可以从一个基类派生也可以从多个基类派生。从一个基类派生称为单继承;从多个基类派生称为多重继承。,2.2继承和派生类,2.2.1单继承 公有继承(public) 公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的。例如: class C: public M intm;/ 声明一个私有数据成员 public: void D();/ 声明一个公有成员函数 ;/

15、 注意分号不能省略 void C: D() m = GetPos();/ 调用基类CMeter的成员函数 coutm ; 从基类M派生的C类除具有M所有公有成员和保护成员外,还有自身的私有数据成员m和公有成员函数D()。 注意:派生类中或派生类的对象可以使用基类的公有成员(包括保护成员),例如C的成员函数D中调用了基类M的GetPos函数。,2.2继承和派生类,私有继承(private) 私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。 保护继承(protected) 特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生

16、类成员函数或友元访问,基类的私有成员仍然是私有的。 注意,一定要区分清楚派生类的对象和派生类中的成员函数对基类的访问是不同的。例如,在公有继承时,派生类的对象可以访问基类中的公有成员,派生类的成员函数可以访问基类中的公有成员和保护成员。在私有继承和保护继承时,基类的所有成员不能被派生类的对象访问,而派生类的成员函数可以访问基类中的公有成员和保护成员。,2.2继承和派生类,继承方式对基类成员的访问控制,2.2继承和派生类,2.2.2多继承 在类的继承中,允许一个派生类继承多个基类,这种多继承的方式可使派生类具有多个基类的特性,大大提高了程序代码的可重用性。 多继承下派生类的定义是按下面的格式:

17、class : , ,. ; 其中的继承方式还是前面的三种:public、private和protected。 例如: class A . class B . class C:public A,private B . 派生类C的成员包含了基类A中成员和B中成员以及该类本身的成员。 允许一个基类有多个派生类以及从一个基类的派生类中再进行多个层次的派生。,2.2继承和派生类,2.2.3派生类的构造函数和析构函数 派生类的构造函数和析构函数被执行时,基类相应的构造函数和析构函数也会被执行。 注意,派生类对象在建立时,先执行基类的构造函数,然后执行派生类的构造函数。但对于析构函数来说,其顺序刚好相反,

18、先执行派生类的析构函数,而后执行基类的析构函数。 需要注意的是,如果在对派生类进行初始化时,需要对其基类设置初值,则可按下列格式进行: (总参表):(参数表1), (参数表2), , (参数表n), 对象成员1(对象成员参数表1), 对象成员2(对象成员参数表2), , 对象成员n(对象成员参数表n) . 构造函数总参表后面给出的是需要用参数初始化的基类名、对象成员名及各自对应的参数表,基类名和对象成员名之间的顺序可以是任意的,且对于使用默认构造函数的基类和对象成员,可以不列出基类名和对象成员名。这里所说的对象成员是指在派生类中新声明的数据成员,它属于另外一个类的对象。对象成员必须在初始化列表

19、中进行初始化。,2.2继承和派生类,派生类的构造函数和析构函数执行次序 #include iostream.h class Base private: int x; public: Base(int a) cout执行基类Base的构造函数endl; x=a; Base()cout执行基类Base的析构函数endl; ;,class A private: int n; public: A(int m) n=m; cout执行类A的构造函数endl; A()cout执行类A的析构函数endl; ; class Derive:public Base private: int y; A a; pub

20、lic: Derive(int a,int b,int c):a(c),Base(a) cout执行派生类Derive的构造函数endl; y=b; Derive()cout执行派生类Derive的析构函数endl; ;,void main() Derive dobj(10,20,30); ,2.2继承和派生类,派生类的构造函数的执行顺序: 1、基类的构造函数 2、对象成员的构造函数 3、派生类的构造函数,2.2继承和派生类,2.2.4虚基类 例 基类成员调用的二义性。 #include class A protected: int a; public: A() a=10;cout“基类A的构

21、造函数被执行”endl; ; class A1 : public A public: A1( ) a=a+1; ; class A2 : public A public: A2() a = a+2; ;,2.2继承和派生类,class B : public A1, public A2 public: B() void print() couta = aendl;/ 编译出错的地方 ; void main() B bobject; bobject.print(); ,2.2继承和派生类,程序中定义了类A,类A1和A2都是在类A的基础上以公有继承方式产生的派生类,而类B是在类A1和A2的基础上经过

22、多重继承方式产生的派生类,所以在类B中数据成员a有两个拷贝,系统无法区分上从类A1继承过来的a还上从A2中继承过来的a,从而出现编译错误。 解决这个问题的方法之一是使用域作用运算符“:”来消除二义性,例如若将print()函数实现代码变为: void print() cout“a = A1:aendl; cout“a = A2:aendl; 重新运行,结果为: A1:a = 11 A2:a = 12 使用虚基类的目的是在多重派生的过程中,使公有的基类在派生类中只有一个拷贝,从而解决上述这种二义性问题。,2.2继承和派生类,例Ex_VirtualBase 虚基类的使用。 #include cla

23、ss A protected: int a; public: A() a=10;cout“基类A的构造函数被执行”endl; ; class A1 : virtual public A public: A1() a= a+1; ; class A2 : virtual public A public: A2( ) a = a+2; ;,2.2继承和派生类,class B : public A1, public A2 public: B() void print() cout“a = aendl; ; void main() B bobject bobject.print(); 运行结果为: 基

24、类A的构造函数被执行 a=13 从以上程序来看,引入了虚基类后,在派生类中数据成员a的值只有一个,不论在类A1中修改还是在A2中修改,都是直接对这唯一一份成员操作。尽管类A1和类A2都是在类A的基础上派生的,但类A的构造函数只执行了一次。,2.3多态和虚函数,虚函数 声明虚函数的方法是在基类中的成员函数原型前加上关键字virtual。格式如下: class 类名 virtual 类型 函数名(参数表); ; 当一个类的成员函数为虚函数后,这就意味着该成员函数在派生类中可能有不同的实现,也就是说,该函数在派生类中可能需要定义与其基类虚函数原型相同的函数。,2.3多态和虚函数,2.3.1虚函数 例

25、 虚函数的使用。 #include class A public: virtual float show()/ 将show定义成虚函数 cout“class A”endl; ; class B:public A public: void show() cout“class B”endl; ; class C :public A public: void show() cout“class C”endl; ;,2.3多态和虚函数,2.3.1虚函数 例 虚函数的使用。 void main() A a; B b; C c; A *p; p= ,2.3多态和虚函数,2.3.1虚函数 程序分析: 在本程

26、序的基类A中定义了虚函数show(),则show()可以在其派生类中有不同的实现。在程序运行中,当用基类的指针指向不同的派生类对象时,系统会自动选择适当的函数,实现运行时的多态性。所以,程序运行结果为: class A class B class C 如果没有声明基类A的成员函数show()为虚函数,则程序的运行结果为: class A class A class A,2.3多态和虚函数,2.3.1虚函数 例 虚函数的使用。 #include class A public: virtual float show()/ 将show定义成虚函数 cout“class A”endl; ; class

27、 B:public A public: void show() cout“class B”endl; ; class C :public A public: void show() cout“class C”endl; ;,2.3多态和虚函数,class CCircle:public CShape public: CCircle(float r) R=r; float area() return (float)(3.14159265 * R * R); private: float R; ; void main() CShape *s2; s0 = new CTriangle(3,4); co

28、utarea()area()endl; 运行结果为: 6 78.5398,2.3多态和虚函数,说明: (1)虚函数在重新定义时参数的个数和类型必须和基类中的虚函数完全匹配。 (2)虚函数所具备的上述功能,只有通过基类指针才可得到。虚函数在用对象名和成员运算符以正常方式调用时,不能达到其功能。例如: CShape ss; ss.area(); 将得到0.0。 (3)如果不使用new来创建相应的派生类对象,也可使用下列方法来实现: void main() CShape *p1, *p2; CTriangle tri(3, 4); CCircle cir(5); p1 = (4)虚函数必须是类的一个

29、成员函数,不能是友元函数,也不能是静态的成员函数。 (5)可把析函数定义为虚函数,但不能将构造函数定义为虚函数。通常在释放基类中和其派生类中的动态申请的存储空间时,也要把析构函数定义为虚函数,以便实现撤消对象时的多态性。,2.3友元,友元函数: 友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,不属于任何类,但需要在类的定义中加以声明,声明时只需在友元函数的名称前加上关键字friend,格式如下: friend 类型函数名(形参);,2.3友元,例: #include #include Class Point Private: double x; double y; P

30、ublic: Point(double a;double b) x=a; y=b; friend double distance(Point a,Point b); ;,2.3友元,double distance(Point a,Point b) return sqrt(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y); void main() Point p1(1,2); Point p2(5,6); cout“The distance is”distance(p1,p2)endl; 程序运行结果为: The distance is 5.65685,2.3友元,1

31、、distance()是类Point的一个友元函数,它可以直接访问类Point的所有成员。 2、友元函数的声明可以放在类的私有部分,也可以放在公有部分,它们是没有区别的,都说明是该类的一个友元函数。 3、一个函数可以是多个类的友元函数,但需要在各个类中分别声明。 4、友元函数的调用与一般函数的调用的方式和原理一致。,2.3友元,友元类: 当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。格式如下: friend class 类名; 如: Class A Public: friend class B; ; 友元关系不具有对称性,即如果类B时类A的友元类,并不隐含类A时类B

32、的友元类。友元关系不具有传递性,如果类B时类A的友元类,而类C时类B的友元类,并不隐含类C时类A的友元类。,2.3多态和虚函数,2.3.2纯虚函数和抽象类 定义一个基类时,有时会遇到情况:无法定义基类中虚函数的具体实现,其实现完全依赖于其不同的派生类。例如,一个“形状类”由于没有确定的具体形状,因此其计算面积的函数也就无法实现。这时可将基类中的虚函数声明为纯虚函数。 声明纯虚函数的一般格式为: virtual () = 0; 与一般虚函数不同的是:在纯虚函数的形参表后面多了个“= 0”。把函数名赋于0,本质上是将指向函数的指针的初值赋为0。虚函数不能有具体的实现代码。 抽象类是指至少包含一个纯

33、虚函数的特殊的类。它本身不能被实例化,也就是说不能声明一个抽象类的对象。必须通过继承得到派生类后,在派生类中定义了纯虚函数的具有实现代码,才能获得一个派生类的对象。 例 虚函数的使用。 #include class CShape public: virtual float area() = 0;/ 将area定义成纯虚函数 ;,2.3多态和虚函数,class CTriangle:public CShape public: CTriangle(float h, float w) H = h;W = w; float area()/ 在派生类定义纯虚函数的具体实现代码 return (float)

34、(H * W * 0.5); private: float H, W; ; class CCircle:public CShape public: CCircle(float r) R = r; float area() / 在派生类定义纯虚函数的具体实现代码 return (float)(3.14159265 * R * R); private: float R; ;,2.3多态和虚函数,void main() CShape *pShape; CTriangle tri(3, 4); coutarea()area()endl; 运行结果为: 6 6 78.5398 78.5398,2.4重载

35、,2.4函数重载 函数重载是指一个函数可以和同一作用域中的其他函数具有相同的名字,但这些同名函数的参数类型、参数个数、函数返回值类型以及函数功能可以完全不同。 由于重载函数具有相同的函数名,在进行函数调用时,系统依据什么来确定所调用的函数是谁呢?系统一般按照调用函数时的参数个数、类型和顺序来确定被调用的函数。 例 。 #include int max(int n1,int n2); double max(double d1,double d2); char max(char c1,char c2); void main() coutn2?n1:n2; ,2.4运算符重载,double max(

36、double d1,double d2) return d1d2?d1:d2; char max(char c1,char c2) return c1c2?c1:c2; 程序的运行结果为: 两个整数中的大的:5 两个双精度数中的大的:5.2 两个字符中的大的:d 从以上程序中可以看出:程序中各种形式的max函数都称为max的重载函数。 定义重载函数时要注意: 1、重载函数间不能只是函数的返回值类型不同,应至少在形参的个数、类型或顺序上有所不同。 2、应使所有的重载函数的功能相同。如果让重载函数完成不同的功能,是不好的编程风格,因为这样会破坏程序的可读性。,2.7运算符重载,2.7.1运算符重载

37、的语法 为了重载运算符,必须定义一个特殊的函数,以便通知编译器,遇到该重载运算符时调用该函数,并由该函数来完成该运算符应该完成的操作。这种函数称为运算符重载函数,通常是类的成员函数或是友元函数,运算符的操作数通常也是该类的对象。 定义一个运算符重载函数函数名必须以operator开头,一般形式如下: :operator () / 函数体 重载的运算符必须是一个合法的运算符,如“+”、“-”、“*”、“/”、“+”等。 例 运算符的简单重载。 #include class CComplex public: CComplex(double r = 0, double i = 0) realPart = r; imagePart = i; void print() cout该复数实部 = realPart, 虚部 = imagePartendl; CComplex operator + (CComplex ,2.7运算符重载,CComplex CComplex:operator + (CComplex 运行结果为: 该复数实部 = 62, 虚部 = 90 该复数实部 = 32, 虚部 = 20,2.7运算符重载,还需要说明的是:

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论