1、1,第2章 C+面向对象 程序设计 2.1 类和对象 2.2 继承和派生类 2.3 多态和虚函数 2.4 运算符重载 2.5 输入/输出流库,2,定义类: class private:public:;,2.1 类和对象,干什么,以C开头,注: 数据不可初始化,3,成员函数在类体外定义,加作用域运算符“:” Class CMeter public:void StepIt();private:int m_nPos; void CMeter:StepIt() m_nPos+; ,2.1 类和对象,4,注意: (1) 数据成员的数据类型任意,但不允许对数据成员进行初始化例: class CMeter
2、.private:int m_nPos = 10; / 错误; (2) 在“public:”或“private:”后面定义的所有成员都是公有或私有的,直到下一个“public:”或“private:”出现为止。默认是private(私有) 。 (3) public和private可以在类中出现多次,前后的顺序没有关系;但最好先公有,后私有。 (4) 三种类型:public,private,protected。 (5) 类的声明放入.h文件,成员函数的实现放入同名的.cpp文件。,2.1 类和对象,5,2.1.2 对象的定义 定义对象: 对象既可以是一个普通对象,也可以是一个数组对象或指针对象。
3、例如:CMeter myMeter, *Meter, Meters2; 访问对象的成员变量和成员函数:在成员前面加上对象名和成员运算符“.”(只能访问公有成员) :.()- ,2.1 类和对象,6,2.1.3 构造函数和析构函数 1. 构造函数 构造函数:变量、对象的初始化。 特点:在对象建立时它会被自动执行。 规定:构造函数必须与相应的类同名,可以带参数,也可以不带参数,与一般的成员函数定义相同,也可以重载 例如: class CMeterpublic:CMeter(int nPos ) / 带参数的构造函数 m_nPos = nPos; . CMeter oMeter(10), oTick
4、(20); 则对象oMeter的m_nPos=10;对象oTick的m_nPos=20。,2.1 类和对象,7,2.1.3 构造函数和析构函数 2. 析构函数 析构函数:与构造函数相对应,在类名称前面加上一个“”符号。每个类只有一个析构函数,没有任何参数,也不返回任何值。 例如: class CMeter public:.CMeter( ) / 析构函数. 析构函数被自动调用的情况: (1) 当对象定义在一个函数体中,该函数调用结束后; (2) 用new为对象分配动态内存,当使用delete释放对象时。,2.1 类和对象,8,2.1.3 构造函数和析构函数 3. 默认构造函数和析构函数 默认构
5、造函数和析构函数(不带参数): CMeter( ) / 默认构造函数的形式 CMeter( ) / 默认析构函数的形式 说明:在用户定义一个对象时,编译器会自动根据对象 定义的格式选择相应的构造函数。 例如:CMeter m1, m2; 默认构造函数将对象的所有数据成员都初始化为零或空,2.1 类和对象,9,4. 构造函数的重载 例Ex_ConOverLoad 构造函数的重载 #include class CDate public:CDate();CDate(int day);CDate(int month, int day);CDate(int year, int month, int da
6、y);/ 其他公共成员private:int nYear, nMonth, nDay; ; CDate:CDate() nMonth = 7; nDay = 30; nYear = 2002;coutnYear“-“nMonth“-“nDayendl; ,2.1 类和对象,10,CDate:CDate(int day) nMonth = 7; nDay = day; nYear = 2002;coutnYear“-“nMonth“-“nDayendl; CDate:CDate(int month, int day) nMonth = month; nDay = day; nYear = 200
7、2;coutnYear“-“nMonth“-“nDayendl; CDate:CDate(int year, int month, int day) nYear = year; nMonth = month; nDay = day; coutnYear“-“nMonth“-“nDayendl; void main() CDate day1;CDate day2(28);CDate day3(8, 1);CDate day4(2003, 3, 10); ,2.1 类和对象,11,例Ex_ConDefault 带默认参数的构造函数 #include class CDate public:CDate
8、(int year = 2002, int month = 7, int day = 30) nYear = year; nMonth = month; nDay = day; coutnYear“-“nMonth“-“nDayendl;/ 其他公共成员private:int nYear, nMonth, nDay; ; void main() CDate day1;CDate day2(2002, 8); ,2.1 类和对象,12,5. 复制构造函数(拷贝构造函数) 复制构造函数:用一个已知的对象来初始化一个被创建的同类的对象。复制构造函数的函数名也是它所属类的类名,但它只有一个参数,且参数
9、是同类的一个对象的引用。 定义形式: :(const&) const是一个类型修饰符,被它修饰的对象是一个不能被更新的常量。,2.1 类和对象,13,例Ex_ConCopy 拷贝构造函数的使用 #include class CDate public:CDate(int year = 2002, int month = 7, int day = 30) cout“调用构造函数“endl; nYear = year; nMonth = month; nDay = day; coutnYear“-“nMonth“-“nDayendl;,2.1 类和对象,14,CDate(const CDate ,2
10、.1 类和对象,15,默认的拷贝构造函数 例Ex_ConCopyDefault 默认拷贝构造函数的使用 #include class CDate public:CDate(int year = 2002, int month = 7, int day = 30) cout“调用构造函数“endl; nYear = year; nMonth = month; nDay = day; void output() coutnYear“-“nMonth“-“nDayendl; private:int nYear, nMonth, nDay; ; void main() CDate day1(2002,
11、 8);CDate day2(day1); / 调用默认的拷贝函数day1.output();day2.output(); ,2.1 类和对象,16,2.1.4 对象成员初始化 把一个已定义类的对象作为另一个类的成员。 构造函数定义格式: :(形参表):对象1(参数表), 对象2(参数表), , 对象n(参数表) 其中,对象1、对象2、对象n就是该类使用的其他类的对象,冒号“:”后面的列表称为成员初始化列表。,2.1 类和对象,17,例Ex_InitMultObject 对象成员的初始化 #include class CPoint public:CPoint(int x, int y) nPo
12、sX = x; nPosY = y; void ShowPos() cout“当前位置:x = “nPosX“, y = “nPosYendl; private:int nPosX, nPosY; ; class CSize public:CSize(int l, int w) nLength = l; nWidth = w; void ShowSize() cout“当前大小:l = “nLength“, w = “nWidthendl; private:int nLength, nWidth; ;,2.1 类和对象,18,class CRect public:CRect(int left,
13、 int top, int right, int bottom):size(right-left, bottom-top),ptCenter(left+right)/2, (top+bottom)/2) void Show() ptCenter.ShowPos(); size.ShowSize(); private:CPoint ptCenter;CSize size; ; void main() CRect rc(10, 100, 80, 250);rc.Show(); ,2.1 类和对象,19,2.1.5 静态成员 1. 静态数据成员 同一个类中所有对象共享的成员。 定义: (1) 使用关
14、键字static声明静态数据成员。 (2) 静态数据成员初始化在类的外部进行。 成员初始化: :=,2.1 类和对象,20,例Ex_StaticData 静态数据成员的使用 #include class CSum public:CSum(int a = 0, int b = 0) nSum += a+b; int GetSum() return nSum; void SetSum(int sum) nSum = sum; private:static int nSum; / 声明静态数据成员 ; int CSum:nSum = 0; / 静态数据成员的初始化 void main() CSum
15、one(10, 2), two;cout“sum = “one.GetSum()endl;one.SetSum(5);cout“sum = “one.GetSum()endl;cout“sum = “two.GetSum()endl; ,2.1 类和对象,21,2. 静态成员函数 静态成员函数属于类的成员,函数中可以引用类中说明的静态成员,如要引用非静态成员,可以通过对象来引用。 例Ex_StaticFunc 静态成员函数的使用 #include class CSum public:CSum(int a = 0, int b = 0) nSum += a+b; int GetSum() ret
16、urn nSum; void SetSum(int sum) nSum = sum; static void ShowData(CSum one); / 声明静态成员函数private:static int nSum; ;,2.1 类和对象,22,void CSum:ShowData(CSum one) / 静态成员函数的实现 cout“直接使用静态成员“endl;cout“sum = “nSumendl;/若nSum不是static,只能用下列方式访问cout“使用同类的对象”endl; cout“sum = “one.GetSum()endl; int CSum:nSum = 0; voi
17、d main() CSum one(10, 2);CSum:ShowData(one); /用所属类名直接访问one.SetSum(8);one.ShowData(one); / 通过对象访问 ,2.1 类和对象,23,2.1.6 友元 friend,类中声明而类外定义,它不是成员函数,但可以访问私有和保护数据成员,破坏了类的封装性。 例Ex_FriendFunc 友元函数的使用 #include class CPoint public:CPoint() m_x = m_y = 0; CPoint( unsigned x, unsigned y ) m_x = x; m_y = y; void
18、 Print() cout “Point(“ m_x “, “ m_y “)“ endl; friend CPoint Inflate(CPoint ,2.1 类和对象,24,CPoint Inflate ( CPoint ,2.1 类和对象,25,2.1.7 常类型 const,值不能被更改,定义或说明的时候必须初始化。 1. 常对象 定义: const 修饰符const可以放在类名后面,也可以放在类名前面。例如: class COne public:COne(int a, int b) x = a; y = b; private:int x, y; ; const COne a(3,4);
19、 COne const b(5,6); 其中,a和b都是COne对象常量,初始化后就不能再被更新。,2.1 类和对象,26,2.1.7 常类型 2. 常指针 const的不同位置: const在指针变量的类型之前,表示声明一个指向常量的指针。此时,在程序中不能通过指针来改变它所指向的数据值,但可以改变指针本身的值。声明时赋不赋初值均可。例如:int a = 1, b = 2;const int *p1 = /指向常量的指针本身的值是可以改变的 const放在指针定义语句的指针名前,表示指针本身是一个常量,称为指针常量或常指针。因此,不能改变这种指针变量的值,但可以改变指变量所指向的数据值,声明
20、时必须赋初值。,2.1 类和对象,27,例如:int a = 1, b = 2;int * const p1 = / 错误,指针值不可变,2.1 类和对象,28,例Ex_ConstPara 常参数的函数传递 #include class COne public:void print(const int *p, int n) / 使用常参数cout“*p;for (int i = 1; in; i+)cout“, “*(p+i);cout“endl; ; void main() int array6 = 1, 2, 3, 4, 5, 6;COne one;one.print(array, 6);
21、 ,2.1 类和对象,29,2.1.7 常类型 3. 常成员函数 常成员函数:使用const进行声明的成员函数。 作用:只有常成员函数有资格操作常量或常对象 函数说明:() const; 注意:const是加在函数说明后面的类型修饰符,它是函数类型的一个组成部分,因此,在函数实现部分也要带const关键字。,2.1 类和对象,30,例Ex_ConstFunc 常成员函数的使用 #include class COne public:COne(int a, int b) x = a; y = b; void print();void print() const; / 声明常成员函数 private
22、:int x, y; ;,2.1 类和对象,31,void COne:print() coutx“, “yendl; void COne:print() const cout“使用常成员函数:“x“, “yendl; void main() COne one(5, 4);one.print();const COne two(20, 52);two.print(); ,2.1 类和对象,32,4. 常数据成员 说明:在类中声明了const数据成员时,只能通过成员初始化列表的方式来完成构造函数对数据成员初始化。 例Ex_ConstData 常数据成员的使用 #include class COne
23、public:COne(int a):x(a),r(x) / 常数据成员的初始化void print();const int / 常数据成员,2.1 类和对象,33,static const int y; / 静态常数据成员 ; const int COne:y = 10; / 静态数据成员的初始化 void COne:print() cout“x = “x“, y = “y“, r = “rendl; void main() COne one(100);one.print(); ,2.1 类和对象,34,2.1.8 this指针 this指针:仅能被类的非静态成员函数访问。当一个对象调用成员
24、函数时,编译器先将对象的地址赋给this指针,然后调用成员函数。例:调用one.copy(two);时,this指针就指向了one这个对象。 说明:通过*this可以判断是哪个对象来调用该成员函数或重新指定对象。,2.1 类和对象,35,例Ex_This this指针的使用 #include class COne public:COne() x = y = 0; COne(int a, int b) x = a; y = b; void copy(COne ,2.1 类和对象,36,void COne:copy(COne ,2.1 类和对象,37,2.1.9 类的作用域和对象的生存期 类的作用
25、域:在类的定义中由 括起来的部分。 文件作用域:全局变量的作用域,自声明处始,文件结 束处终。 类作用域与文件作用域: 相似:作用域中可以定义变量和函数 不同:类作用域中定义的变量不能使用auto, register 和extern修饰符,只能使用static,2.1 类和对象,38,对象生存期:对象从被创建到被释放的时间。 (1) 局部对象:当对象被定义时调用构造函数,该对象被创建,当程序退出定义该对象所在的函数体或程序块时,调用析构函数,释放该对象。 (2) 静态对象:当程序第一次执行所定义的静态对象时,该对象被创建,当程序结束时,该对象被释放。 (3) 全局对象:当程序开始时,调用构造函
26、数创建该对象,当程序结束时调用析构函数释放该对象。,2.1 类和对象,39,基类、派生类、单继承、多继承 注意:基类或基类的对象不能使用派生类的成员 2.2.1. 单继承 从一个基类定义一个派生类:class : ; 继承方式:public(公有)、private(私有)、protected(保护),默认为private方式,2.2 继承和派生类,40,程序验证默认继承方式: #include class A public:int mpos; ; class B : A public:int npos; ; void main() B b1;b1.mpos=20; ,2.2 继承和派生类,41
27、,1. 公有继承(public) 特点:基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的。 class CStick : public CMeter int m_nStickNum; / 默认为私有数据成员public:void DispStick(); / 声明一个公有成员函数 ; void CStick: DispStick() m_nStickNum = GetPos(); / 调用基类CMeter的成员函数coutm_nStickNum ; ,2.2 继承和派生类,42,例Ex_ClassPublicDerived 派生类的公有继承示例 #
28、include class CMeter public:CMeter(int nPos = 10) m_nPos = nPos; CMeter() void StepIt() m_nPos+; int GetPos() return m_nPos; protected:void SetPos(int nPos) m_nPos = nPos; private:int m_nPos; ; class CStick : public CMeter / 从CMeter派生,公有继承 int m_nStickNum; / 声明一个私有数据成员public:void DispStick(); / 声明一个
29、公有成员函数void SetStick(int nPos) SetPos(nPos); / 类中调用基类的保护成员 ;,2.2 继承和派生类,43,void CStick: DispStick() m_nStickNum = GetPos(); / 调用基类CMeter的成员函数coutm_nStickNum ; void main() CMeter oMeter(20);CStick oStick;cout“CMeter:“oMeter.GetPos()“,CStick:“oStick.GetPos()endl;oMeter.StepIt();cout“CMeter:“oMeter.GetP
30、os()“,CStick:“oStick.GetPos()endl;oStick.StepIt();out“CMeter:“oMeter.GetPos()“,CStick:“oStick.GetPos()endl;oStick.DispStick();oStick.StepIt();oStick.DispStick(); ,2.2 继承和派生类,44,2. 私有继承(private) 私有继承:基类的公有成员和保护成员都作为派生类的私有成员,因此无法再往下继承。 思考:派生类对象、派生类中的成员函数可以访问基类中的 哪些成员? 例Ex_ClassPrivateDerived 派生类的私有继承示
31、例 #include class CMeter public:CMeter(int nPos = 10) m_nPos = nPos; CMeter() void StepIt() m_nPos+; int GetPos() return m_nPos; protected:void SetPos(int nPos) m_nPos = nPos; ,2.2 继承和派生类,45,private:int m_nPos; ; class CStick : private CMeter / 从CMeter派生,私有继承 int m_nStickNum; / 声明一个私有数据成员 public:void
32、 DispStick(); / 声明一个公有成员函数void SetStick(int nPos)SetPos(nPos); / 调用基类的保护成员int GetStick() return GetPos(); / 调用基类的公有成员 ;,2.2 继承和派生类,46,void CStick: DispStick() m_nStickNum = GetPos(); / 调用基类CMeter的成员函数coutm_nStickNum ; void main() CMeter oMeter(20);CStick oStick;cout“CMeter:“oMeter.GetPos()“,CStick:“
33、oStick.GetStick()endl;oMeter.StepIt();cout“CMeter:“oMeter.GetPos()“,CStick:“oStick.GetStick()endl;oStick.DispStick(); ,2.2 继承和派生类,47,3. 保护继承(protected) 特点:基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问。,2.2 继承和派生类,48,2.2.2 派生类的构造函数和析构函数 例Ex_ClassDerived 派生类的构造函数和析构函数的示例 #include #include class CAnima
34、l public:CAnimal(char *pName = “noname“);CAnimal();void setName(char *pName) strncpy(name, pName, sizeof(name); char *getName(void) return name; private:char name20; ; CAnimal:CAnimal(char *pName) setName(pName);cout“调用CAnimal的构造函数!“endl; ,2.2 继承和派生类,49,CAnimal:CAnimal() cout“调用CAnimal的析构函数!“endl; c
35、lass CCat : public CAnimal public:CCat() cout“调用CCat的构造函数!“endl; CCat() cout“调用CCat的析造函数!“endl; void DispName() cout“猫的名字是:“getName()endl; ; void main() CCat cat;cat.DispName();cat.setName(“Snoopy“);cat.DispName(); ,2.2 继承和派生类,50,2.2.3 多继承 定义: class : , , . ; 继承方式:public、private和protected。 例如:class
36、A.class B.class C:public A,private B.,2.2 继承和派生类,51,2.2.4虚基类 基类成员调用的二义性:多继承下,对基类中某成员的访问可能出现不唯一的情况 例Ex_Conflict 基类成员调用的二义性 #include class A public:int x;A(int a = 0) x = a; ; class B1 : public A ;,2.2 继承和派生类,52,class B2 : public A ; class C : public B1, public B2 public:void print()cout“x = “xendl; /
37、 编译出错的地方 ; void main() C c1(100,200,300,400,500);c1.print(); ,2.2 继承和派生类,53,2.2.4 虚基类 解决二义性的方法之一是使用域作用运算符“ :” ,例如将print()函数实现代码变为:void print()cout“B1:x = “B1:xendl;cout“B2:x = “B2:xendl;,2.2 继承和派生类,54,2.2.4 虚基类 解决二义性的方法二:虚基类(虚继承) 声明虚基类的格式: Virtual 特点: 所有派生类中只有一个基类的复制 例Ex_VirtualBase 虚基类的使用 #include
38、class A public: int x; A(int a=0) x=a; ;,2.2 继承和派生类,55,class B1 : virtual public A void print(void) cout“B1:x=”xendl; ; class B2 : virtual public A void print(void) cout“B1:x=”xendl; ; class C : public B1, public B2 ;,2.2 继承和派生类,56,多态性:不同类型的对象接收相同的信息时产生不同的行为。例如函数重载。分类:(1) 编译时的多态性:函数重载或运算符重载。(2) 运行时的
39、多态性:虚函数。即在程序执行之前,根据函数和参数无法确定应该调用哪个函数,必须在程序的执行过程中,根据执行情况动态的确定。 联编:将一个标识符和一个存储地址联系在一起的过程。分类:(1) 静态联编:联编在编译阶段完成。(2) 动态联编:联编在程序运行时动态进行。,2.3 多态和虚函数,57,虚函数: 定义:函数前加上 virtual 。 作用:通过虚函数,达到用基类指针访问派生类对象成员函数的目的。从而使一个函数具有不同的版本,不同版本在该基类的派生类中重新定义,实现了程序的运行时多态。,2.3 多态和虚函数,58,2.3.1 虚函数 例Ex_VirtualFunc 虚函数的使用 #inclu
40、de class CShape public:virtual float area() / 将area定义成虚函数 return 0.0; ; class CTriangle:public CShape public:CTriangle(float h, float w) H=h; W=w; float area() return (float)(H * W * 0.5); ,2.3 多态和虚函数,59,private:float H, W; ; class CCircle:public CShape public:CCircle(float r) R=r; float area() retu
41、rn (float)(3.14159265 * R * R); private:float R; ; void main() CShape *s2;s0 = new CTriangle(3,4);coutarea()area()endl; ,2.3 多态和虚函数,60,说明: 虚函数在重新定义时参数须和基类中的虚函数完全匹配。 只有通过基类指针才能实现虚函数的多态性。 如果不使用new来创建相应的派生类对象指针,也可通过&运算符来获取对象的地址。 虚函数必须是类的一个成员函数,不能是友元函数,也不能是静态的成员函数。 析构函数可定义为虚函数,构造函数不能。,2.3 多态和虚函数,61,2.3.
42、2 纯虚函数和抽象类 声明纯虚函数:virtual () = 0; / 本质是将指向函数的指针的初值赋为0 纯虚函数不能有具体的实现代码 抽象类:至少包含一个纯虚函数的类。不能声明一个抽象类的对象。必须在派生类中定义了纯虚函数的具体代码,才能获得派生类的对象。,2.3 多态和虚函数,62,例Ex_PureVirtualFunc 纯虚函数和抽象类的使用 #include class CShape public:virtual float area() = 0; / 将area定义成纯虚函数 ; class CTriangle:public CShape public:CTriangle(floa
43、t h, float w) H = h; W = w; float area() / 在派生类定义纯虚函数的具体实现代码 return (float)(H * W * 0.5); private:float H, W; ;,2.3 多态和虚函数,63,class CCircle:public CShape public:CCircle(float r) R = r; float area() / 在派生类定义纯虚函数的具体实现代码 return (float)(3.14159265 * R * R); private:float R; ; void main() CShape *pShape;
44、CTriangle tri(3, 4);coutarea()area()endl; ,2.3 多态和虚函数,64,运算符重载函数: 通常是类的成员函数或友元,操作数通常是该类的对象 定义: :operator () / 函数体 例Ex_Complex 运算符重载实现复数的相加 #include class CComplex public:,2.4 运算符重载,65,CComplex(double r = 0, double i = 0) realPart = r; imagePart = i; void print()cout“该复数实部 = “realPart“, 虚部 = “imagePa
45、rtendl;CComplex operator + (CComplex ,2.4 运算符重载,66,CComplex CComplex:operator + (CComplex ,2.4 运算符重载,67,return temp; void main() CComplex c1(12,20), c2(50,70), c;c = c1 + c2; /解释为c1.operator +(c2)c.print();c = c1+ 20;c.print(); ,2.4 运算符重载,68,说明: 1. 运算符重载函数是类的成员函数时,该函数的形参个数比运算符操作数个数少1。 2. 用成员函数实现运算符重
46、载时,左操作数一定是对象,右操作数可以是对象、对象的引用或其他类型的参数。 3. ?:、. 、* 、:、sizeof不允许重载 4. 只能对C+中已定义的运算符进行重载,且不可改变运算符的操作数个数、优先级和结合性。,2.4 运算符重载,69,2.4.2 友元重载 运算符重载的方法:类的成员函数、类的友元函数 格式: friend operator ()/ 单目运算符重载,形参为对象或引用 / 函数体friend operator () / 双目运算符重载 / 函数体 说明: =、()、和-,不能重载为友元函数,2.4 运算符重载,必须有一个是类的对象,70,例Ex_ComplexFriend
47、 运算符的友元重载 #include class CComplex public:CComplex(double r = 0, double i = 0) realPart = r; imagePart = i; void print() cout“该复数实部 = “realPart“, 虚部 = “imagePartendl; friend CComplex operator + (double r, CComplex / 友元重载+,2.4 运算符重载,71,friend CComplex operator - (CComplex ,2.4 运算符重载,72,CComplex operator - (CComplex /友元 ,2.4 运算符重载,73,2.4.3 转换函数 类型转换:将一种类型的值映射为另一种类型的值 转换函数:将类对象转化成指定的数据类型; class public:operator (); operator和类型一起构成了转换函数名,