1 / 54

目 录

第 1 章 C++ 概述 第 2 章 数据类型、运算符和表达式 第 3 章 简单的输入 / 输出 第 4 章 C++ 的流程控制 第 5 章 函数 第 6 章 编译预处理 第 7 章 数组 第 8 章 结构体、共同体和枚举类型 第 9 章 指针和引用 第 10 章 类和对象 第 11 章 类和对象的其他特性 第 12 章 继承和派生 第 13 章 多态性 第 14 章 输入 / 输出流 第 15 章 模板. 目 录. 第一部分 面向过程的程序设计.

Télécharger la présentation

目 录

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第1章 C++概述 第2章 数据类型、运算符和表达式 第3章 简单的输入/输出 第4章 C++的流程控制 第5章 函数 第6章 编译预处理 第7章 数组 第8章 结构体、共同体和枚举类型 第9章 指针和引用 第10章 类和对象 第11章 类和对象的其他特性 第12章 继承和派生 第13章 多态性 第14章 输入/输出流 第15章 模板 目 录 • 第一部分 面向过程的程序设计 • 第二部分 面向对象的程序设计

  2. 第10章 类和对象

  3. 10.1 类和对象的定义 10.1.1 从结构体到类 以前,定义结构体类型,描述一个对象 : struct SPerson //只有数据成员 { char name[20]; //姓名 char sex; //性别 int age; //年龄 }; 我们把一个对象可能具有的动作,加入到对象的描述中,就形成了类。

  4. 10.1.2 类和对象的定义形式 class <类名> { [ [private:] //私有成员,缺省存取权限 <数据成员及成员函数> ] [ public: //公有成员 <数据成员及成员函数> ] [ protected: //保护成员 <数据成员及成员函数> ] }; 私有成员:只允许类内成员函数存取它 公有成员:允许类内和类外函数存取它 保护成员:允许类内和其派生类函数存取它

  5. #include <iostream.h> #include <string.h> class Person { private:// 此处,private可缺省 char Name[20]; //姓名 char Sex;//性别,三个数据成员 int Age;//年龄 public:// 以下定义了四个成员函数 void SetData(char n[ ], char s, int a) { strcpy(Name, n);//直接访问Name Sex=s;//直接访问Sex 访问特性 Age=a;//直接访问Age } [例10.1] 定义描述一个人的类Person,源程序文件名person.h

  6. void GetName( char *n ) // 函数 { strcpy(n, Name); }   char GetSex( ) // 函数 { return Sex; } int GetAge( ) // 函数 { return Age; } };// 注意:类定义结束处的分号不能少

  7. 类是一种类型,该类型的变量称为对象(实例)类是一种类型,该类型的变量称为对象(实例) 对象的定义: <类名> <对象列表>; 如:Person a, b ; // 定义a、b两个对象 可以定义对象的指针和对象数组: 如:Person *pa, *pb, x[10]; // pa和pb是两个Person类型指针, x是对象数组 pa=&a; pa=x; pb=&b; pb=x; 那么能否这样访问对象a的成员呢? a.Name, a.Sex, a.Age 回答是:不能!

  8. 访问权限 类内访问特性 类外访问特性 10.1.3 对象成员的访问 private 可直接访问 不可直接访问 protected 可直接访问 不可直接访问 public 可直接访问 可直接访问 返回例10.1 表 10-1 类成员的访问特性 Person a, *pa=&a; //类外访问如下, a.name × pa->name × a.SetData(…) √ pa-> SetData(…) √

  9. 10.1.4 成员函数的定义 例10.1,类体内定义成员函数。例10.2 ,类体外定义成员函数。 在类体内 进行 成员函数说明 [例10.2]定义Person类,在类体外定义成员函数, #include <iostream.h> 程序文件名为person.h #include <string.h> class Person { char Name[20]; //姓名 //注意缺省 private char Sex; //性别 int Age; //年龄 public: void SetData(char [ ], char , int ); void GetName(char *); char GetSex( ); int GetAge( ); };

  10. 限定SetData( )函数 属于Person类 // 在类体外定义成员函数 void Person::SetData(char n[ ], char s, int a) { strcpy(Name, n); Sex=s; Age=a; } void Person::GetName( char *n ) { strcpy(n, Name); }

  11. char Person::GetSex( ) { return Sex; } int Person::GetAge( ) { return Age; }

  12. 通过对象 访问 公有成员 例10.3 测试Person类 #include "person.h" //包含例10.1或例10.2中的头文件 void main( ) { Person a, *pa; char name[20]; a.SetData("Cheng", 'F', 20); a.GetName(name); cout << "Name: " << name <<endl; cout << " Sex: " << a.GetSex( ) << endl; cout << " Age: " << a.GetAge( ) << endl;

  13. 通过指针 访问 公有成员 // Person a, *pa; pa=&a; pa->SetData("Zhang", 'M', 18); pa->GetName(name); cout << "Name: " << name <<endl; cout << " Sex: " << pa->GetSex( ) << endl; cout << " Age: " << pa->GetAge( ) << endl; }

  14. [例10.4] 定义并测试长方形类Crect, 长方形是由左上角坐标(left, top) 和右下角坐标(right, bottom)组成。 (left, top) ● ● (right, bottom) 见 “第10章 类和对象(例子).doc”

  15. a pa→ b pb→ "Cheng" 'F' 20 "Zhang" 'M' 18 10.1.5 对象的存储空间 定义对象后,对象数据成员占用不同的存储空间。 Person a, b, *pa=&a, *pb=&b; a.SetData("Cheng", 'F', 20); b.SetData("Zhang", 'M', 18); 该类的所有对象的同一成员函数, 共享同一代码空间。

  16. 10.1.6 定义类和对象的有关说明 1.类中数据成员的类型可以是任意的, 但自身类的对象不可以作为成员。 class Obj { int x; double y; }; class Person { char Name[20]; char Sex; int Age; Obj obj1, obj2; // 可以 Person a, b; // 不可以 Person *pa; // 可以 ...... };

  17. 10.1.6 定义类和对象的有关说明(续) 2.若类的使用在前,定义在后,则需引用性说明。 class B; // B类说明 class A // A类的定义 { private: B *pb; // B类对象的指针pb是A类的数据成员 public: ...... }; class B // B类的定义 { ...... };

  18. 10.1.6 定义类和对象的有关说明(续) 3.定义对象的三种方法 : (1)类的定义完成后,定义对象 class A // A类的定义 { int x; float y; public: ...... }; A a1, a2, a3; //定义A类对象

  19. 10.1.6 定义类和对象的有关说明(续) (2)在定义类的同时定义对象 class A // A类的定义 { int x; float y; public: ...... } a1, a2, a3; //定义A类对象

  20. 10.1.6 定义类和对象的有关说明(续) (3)定义无类名的对象 class //无类名 { int x; float y; public: ...... } a1, a2, a3; //定义该类对象 缺点:只能定义一次该类对象, 如果以后需要再次定义该类对象就不可以了。

  21. 10.1.6 定义类和对象的有关说明(续) 4.结构体与类的区别: 结构体成员缺省的存取权限是公有的, 而类中成员缺省的存取权限是私有的。 结构体是类的特例。

  22. 10.2 初始化对象、撤消对象 “初始化是指:定义对象的同时为其赋初值”。 对结构体类型的对象,可以这样初始化: struct SPerson //定义结构体类型 { char name[20]; //姓名 char sex; //性别 int age; //年龄 }; SPerson p1={ “Cheng”, ‘F’, 20 };//初始化 √

  23. 而对“类”类型的对象的私有成员,不可以这样初始化。而对“类”类型的对象的私有成员,不可以这样初始化。 class Person //定义“类”类型 { char name[20]; //姓名 char sex; //性别 int age; //年龄 }; Person p={ “Cheng”, ‘F’, 20 };//初始化 × 原因: 结构体默认的成员访问特性是公有的, 而类的成员的访问特性是私有的。

  24. 对“类”类型的对象的公有成员,可以这样初始化。对“类”类型的对象的公有成员,可以这样初始化。 class Person //定义“类”类型 { public: char name[20]; //姓名 char sex; //性别 int age; //年龄 }; Person p={ “Cheng”, ‘F’, 20 };//初始化 √ 可以为“类”定义构造函数用于初始化对象的数据成员, 同时还可以定义析构函数用于撤销该对象时做清理工作。

  25. 10.2.1 构造函数和析构函数 1.构造函数 在类体内定义构造函数的一般格式是: ClassName(<形参列表>) // ClassName是类名, { ...... } //在此处作为构造函数名 在类体外定义构造函数的一般格式是: ClassName :: ClassName(<形参列表>) { ...... } 构造函数的调用时机:当产生新对象时,自动调用。 [例10.5] 见 “第10章 类和对象(例子).doc”

  26. 构造函数的特点如下: (1) 构造函数是成员函数,函数体可写在类体内, 也可写在类体外。 (2) 构造函数是特殊函数,该函数的名字与类名相同, 不指定返回值类型。 (3) 构造函数可以重载, 即可以定义多个参数个数不同的构造函数。 (4) 在创建对象时,系统自动调用构造函数。

  27. 2.析构函数 在类体内定义析构函数的一般格式是: ~ClassName( ) // ClassName是类名 { ...... } // 析构函数名为~ClassName 在类体外定义析构函数的一般格式是: ClassName :: ~ClassName( ) { ...... } 析构函数的调用时机:当撤销对象时,自动调用。 [例10.6]见 “第10章 类和对象(例子).doc”

  28. 析构函数的特点如下: (1) 析构函数是成员函数,可在类体内定义, 也可在类体外定义。 (2) 一般地,将析构函数定义成公有成员函数。 (3) 析构函数也是特殊函数,该函数的名字是类名前加 ~, 用来与构造函数区分,该函数不指定返回值类型, 没有参数。 (4) 一个类只能定义一个析构函数, 即析构函数不允许重载。

  29. 析构函数的特点如下(续): (5)析构函数可以被显式调用,也可以由系统自动调用。 在下面两种情况下,析构函数会被自动调用: ①当对象是系统自动创建的,则在对象的作用域结束时,系统自动调用析构函数。 ②当一个对象是使用new运算符被动态创建的,在使用delete运算符释放它时,delete将会自动调用析构函数。

  30. 3.调用构造函数和析构函数的时机(对象的生存周期) 不同存储类型的对象调用构造函数及析构函数 的时机不同。 (1)全局对象(总是静态的) —— 程序main( )开始执行时, 调用构造函数,程序main( )结束时,调用析构函数。 (2)局部动态对象 —— 当程序执行进入作用域, 在定义对象处系统自动创建对象,调用构造函数; 退出作用域时,系统在撤消对象前调用析构函数。 (3)局部静态对象 —— 当程序执行首次到达定义对象处, 系统自动创建对象,调用构造函数;程序结束时 (即main( )函数执行结束时), 系统在撤消对象前调用析构函数。

  31. (4)动态申请的对象 —— 使用 new 产生对象时, 系统自动调用构造函数;使用 delete 撤消对象时, 系统自动调用析构函数。 [例10.7]见 “第10章 类和对象(例子).doc”

  32. 此程序运行结果是: Constructor: 2008.4.4 //调用构造函数,产生d4对象 进入 main( )函数! Constructor: 2008.1.1 // 调用构造函数,产生d1对象 进入 fun( )函数! // 第1次进入fun( )函数,产生下述 d2, d3对象 Constructor: 2008.2.2 Constructor: 2008.3.3 退出 fun( )函数! // 退出fun( )函数,撤消d3对象,不撤消d2对象 Destructor: 2008.3.3

  33. 续: 进入 fun( )函数! // 第2次进入fun( )函数,再次产生d3对象 Constructor: 2008.3.3 退出 fun( )函数! Destructor: 2008.3.3 // 退出fun( )函数,撤消d3对象 退出 main( )函数! // 退出main ( )函数,撤消d1, d2, d4对象 Destructor: 2008.1.1 Destructor: 2008.2.2 Destructor: 2008.4.4

  34. 10.2.2缺省构造函数和缺省析构函数 1. 缺省构造函数 定义类时,若没有定义构造函数,则编译系统 自动生成一个不带参数的缺省构造函数,格式如下: ClassName:: ClassName( ) { }

  35. [例10.8]定义类时,不定义构造函数 #include <iostream.h> class Date { int Year, Month, Day; public: void ShowDate( ) { cout <<Year<<'.'<<Month<<'.'<<Day<<endl; } }; void main( ) { Date d; d.ShowDate( ); } 原因: 系统自动产生的缺省构造函数是: Date::Date( ) { } //不做任何操作 程序的运行结果是:? -858993460.-858993460.-858993460

  36. 缺省构造函数有两种形式: 没有参数的构造函数或各参数均有缺省值的构造函数, 缺省构造函数只能有一个。 Date:: Date( ) //没有参数 { Year=2003; Month=1; Day=1; } 或 Date:: Date(int y=2003, int m=1, int d=1) //均有缺省值 { Year=y; Month=m; Day=d; } 运行结果? void main( ) { Date d; d.ShowDate( ); }

  37. 注意: ①在产生对象时,若不需要对数据成员进行初始化,可以不显式地定义缺省构造函数。 ②在一个类的定义中,缺省构造函数只能有一个。 ③若已经定义了构造函数(不论它是否为缺省构造函数),则编译系统不再自动生成缺省构造函数。

  38. 格式为: ClassName :: ~ ClassName ( ) { }; 2. 缺省析构函数 若在类中没有显式定义析构函数, 则编译器自动地产生一个缺省的析构函数。 ① 在撤消对象时,若不需要做任何结束工作, 可以不定义析构函数。 ② 当类中有动态申请的数据空间时,需要显式地定义 析构函数,撤销动态数据空间。如:例10.6

  39. 格式: //注意参数是本类对象的引用 ClassName :: ClassName( [const]ClassName & Obj ) { <函数体> } 10.2.3 拷贝构造函数和缺省拷贝构造函数 拷贝构造函数:用一个已知的对象来初始化一个 被创建的同类对象。 [例10.9]见 “第10章 类和对象(例子).doc” 如果用户不定义拷贝构造函数,则系统自动产生 一个缺省的拷贝构造函数。如例10.9 : Point::Point(const Point &p) { x=p.x; y=p.y; } //各成员逐一赋值

  40. 当类中有动态申请的数据空间时, 必须定义拷贝构造函数,否则出错。 [例10.10]见 “第10章 类和对象(例子).doc” 自动产生的构造函数如下: Student::Student(const Student &s) { Name = s.Name; // 注意:直接赋地址值 Age = s.Age; }

  41. 何时必须定义拷贝构造函数呢?分两种情况: (1)若只需拷贝同类型对象的部分数据成员。 (2)或者类中的某个数据成员是使用 new 运算符 动态地申请存储空间进行赋初值时。 ★则必须在类中显式地定义一个完成拷贝功能的 构造函数,以便实现成员数据的复制。 同时应显式定义相应的析构函数,撤消动态分配的空间。

  42. 10.2.4 拷贝构造函数的调用时机 在产生新对象时,自动调用拷贝构造函数。 何时产生新对象呢? [例10.11]见 “第10章 类和对象(例子).doc” (1)明确表示由一个对象初始化另一个对象时, 例如:例 10.9 中的A、B两行 Point p3(p1); // A 调用拷贝构造函数 Point p4=p2; // B 调用拷贝构造函数 (2)当用对象作为函数参数时, 系统处理成用实参对象初始化形参对象。 (3)当函数返回对象时, 用返回值对象初始化内存临时对象。

  43. 10.2.5 利用构造函数进行类型转换 C++中不同的数据类型可以相互赋值。若定义了复数类对象 Complex c; 能否写 c = 5.0; 即希望将 5.0做为实部,0做为虚部的一个复数赋值给复数 c。 可以!!!自动类型转换 c = Complex(5.0); //利用构造函数进行类型转换 [例10.13]见 “第10章 类和对象(例子).doc”

  44. 10.3 成员函数的特性 10.3.1 内联函数和外联函数 在类体内定义的成员函数是内联函数, 在类体外定义的函数是外联函数 在类体外,也可以定义内联函数 [例10.14] 在类体外定义内联函数 class Complex { double Real, Image; public: //在类体内,给出成员函数说明 Complex(double x=0, double y=0); void Show( ); };

  45. //在类体外定义内联函数 inline Complex::Complex(double x, double y) { Real=x; Image=y; } inline void Complex::Show( ) { cout<<'('<<Real<<','<<Image<<')'; } 内联函数的优点: 编译时在调用函数处用内联函数的代码来替换函数调用语句, 这样在程序执行时,不需要函数调用,减少时间开销。

  46. 10.3.2 成员函数的重载 构造函数可以重载,一般的成员函数也可以重载。 例10.15处理一个数组构成的线性表,动态产生线性表, 并输出线性表中的数据。 线性表:动态产生的一维整型数组,存放若干个数值, 指针 ListPtr 指向该线性表的第0个元素 。 线性表的长度 nMax 是一维数组的长度, 线性表的元素个数 nElem 是当前已有元素个数, 完成的功能:加入元素、取出元素、给元素重新赋值等。 [例10.15]见 “第10章 类和对象(例子).doc”

  47. 10.4 构造函数和对象成员 • 定义一个类时,可以把一个已定义的类的对象作为该类的成员。 • 产生新定义类的对象时,须对它的对象成员进行初始化。 • 只能通过其对象成员的构造函数来实现。 [例10.16]见 “第10章 类和对象(例子).doc” • 先调用对象成员的构造函数,再调用对象自身的构造函数。 • 析构函数的调用顺序相反。

  48. 在类的定义中,说明对象成员的一般格式为: class ClassName { ClassName_1 c1; ClassName_2 c2; ...... ClassName_n cn; public: ClassName(args) : c1 (arg_1), c2 (arg_2),...cn (arg_n) // A {......} ...... }; 其中,ClassName_1、ClassName_2、......ClassName_n 是已经定义的类名。

  49. 注意: ①args中的形参必须带有类型说明,而 arg_1 、arg_2、......、arg_n 是实参,不需要类型说明,它们可以是来自于args的变量,也可以是常数或表达式等。 ②调用对象成员构造函数的顺序与写在成员初始化列表中的顺序无关,而与对象成员的定义顺序有关,先定义的先调用。如将例10.16中的A行改写为: Line(int x1, int y1, int x2, int y2, int w, int c) : p2(x2, y2), p1(x1, y1)// A 仍然是先调用p1(x1, y1),再调用p2(x2, y2),因为p1的定义在前,p2的定义在后。

  50. 10.5 this 指针 this是一个隐含于每一个类的成员函数中的特殊指针, 它指向调用该函数的对象。 当对象调用成员函数时,自动将对象自身的地址(指针) ,传递给成员函数,在成员函数中可直接使用该指针, 指针名为this。 如有一个类: #include <iostream.h> class Sample { int x, y;

More Related