• 1. 《C++面向对象程序设计》教学内容 第1章 C++概述 第2章 类和对象 第3章 面向对象程序设计概述 第4章 进一步学习类和对象 第5章 堆与复制构造函数 第6章 继承性:派生类 第7章 运算符重载 第8章 虚函数和多态性 第9章 模板 第10章 类库和C++的标准模板库STL 第11章 输入输出流 第12章 异常处理
  • 2. 第2章 类和对象2.1 类和对象的定义 2.2 构造函数和析构函数 2.3 类与const
  • 3. 对象和类属于同一个类的所有对象具有某些共性和相似的特征。一个类定义了一组大体上相似的对象。 在面向对象的软件系统中,对象是基本的运行时实体,它既包含数据,也包括作用于这些数据的操作。
  • 4. 对象对象的组成操作代码数据
  • 5. 面向对象的软件系统对象4对象1对象3对象2
  • 6. §2.1 类和对象的定义2.1.1  C++中对结构的扩展 2.1.2  C++中类的定义 2.1.3  C++类中的成员函数定义 2.1.4  C++中对象的定义和使用 2.1.5  C++中类的接口与实现 2.1.6  类声明与类定义 2.1.7  结构struct与类class的比较
  • 7. 2.1.1  C++中对结构的扩展C++中的结构不仅可以包含不同类型的数据,而且还可以包含函数。 结构中的数据和函数都是结构的成员,分别称为数据成员和函数成员。 在C++中,通常把函数成员称为成员函数。
  • 8. C的结构体struct与C++的struct的比较C语言的结构体中数据与操作是分离的 在C++语言中将数据与操作封装在一个结构体中 struct Student { int number; char name[15]; float score; }; 数据成员 struct Student { int number; char name[15]; float score; void display( )//函数成员 { cout<<”number: ”<< number; cout<<”name: ”<< name; cout<<”score: ”<< score <number); printf(”name:%s”,stu->name); printf(”score:%f\n”,stu->score); } 成员函数数据成员
  • 9. 2.1.2  C++中类的定义在C++语言中,我们通过定义新的数据类型来实现类。 类既包含数据内容又包含对数据的操作,所以类是一个抽象数据类型。 在C++语言中,类可被视为一种用户定义的类型。
  • 10. C ++的结构体struct与C++的class的比较将数据与操作封装在一个C++的结构体struct中 将数据与操作封装在一个C++类class中 数据成员 struct Student { int number; char name[15]; float score; void display( )//函数成员 { cout<<”number: ”<< number; cout<<”name: ”<< name; cout<<”score: ”<< score <
  • 11. 一个复数结构的例子struct complex{ double real; double image; void init(double r,double i) { real=r; image=i; } double realcomplex() { return real; } …; };成员函数数据成员
  • 12. 私有成员和公有成员在C++ 中一个结构的成员通常分为两类: 私有成员( private )和公有成员( public ) 私有成员只能被该结构中的其他成员访问。 公有成员既可以被结构内的其他成员访问,也可以被结构外的成员所访问。 C++规定,在缺省情况下,结构中的成员是公有的。 此处所指成员包括数据成员和成员函数。
  • 13. 私有成员和公有成员的声明struct complex{ private: double real; double image; public: void init(double r,double i) { real=r; image=i; } double realcomplex( ) { return real; } …; };私有成员公有成员
  • 14. 类的定义class 类名{ [ private: ] 私有数据成员和成员函数 public: 公有数据成员和成员函数 }; //分号是不能少的 //其中,class是声明类的关键字 //类名是要声明的类的名字
  • 15. 类中私有成员和公有成员的声明class complex{ private: double real; double image; public: void init(double r,double i) { real=r; image=i; } double realcomplex() { return real; } …; };私有成员公有成员
  • 16. 定义类时的注意事项private和public可以按任意顺序出现 具有良好习惯的程序员会: 把所有私有成员和公有成员归类放在一起 将私有成员放在公有成员的前面 private、public和protected称为访问控制符。 数据成员可以是任何数据类型,但不能用auto、register或extern进行说明。 只有在类对象定义之后才能给数据成员赋初值。
  • 17. 为什么要用类代替结构?在缺省情况下 ,类中的成员是私有的private。 类提供了缺省的安全性。
  • 18. 2.1.3  C++类中的成员函数定义C++类中的成员函数描述了对类对象内部数据的操作方法,或者说,描述了对象的行为,因此我们又将成员函数称为方法(method)或者服务(service)。 【例2.2】学生类中成员函数的定义。
  • 19. 成员函数的定义包含在类体中 class Student{ private: int number; char name[15]; float score; public: void init(int number1,char *name1,float score1); void modify(float score1) { score=score1; } void print( ); };
  • 20. 成员函数在类体外定义 void Student::init(int number1,char *name1,float score1) {作用域解析运算符 number=number1; strcpy(name,name1); score=score1; } void Student::print( ) { cout<<”number: ” <
  • 21. 成员函数在类体内外的区别一般情况下,在类体中仅给出成员函数的原型,而把成员函数的定义放在类体之外实现。 这种将类的成员函数的声明(declaration)和定义(definition)进行分离的方式有很多好处 类体内定义的成员函数在编译时是以内联函数处理的, 只有那些非常简短的函数才在类体中直接定义。
  • 22. 2.1.4  C++中对象的定义和使用在C++语言中,类是用户定义的一种新类型,所以可以象声明普通变量一样来建立对象。 在C++语言中,类与对象的关系就好象整型int 和整型变量i 之间的关系一样。 注意:所有类对象都共享它们的成员函数,但是,每一个对象都建立并保持自己的数据。对象的数据成员,在C++中称为实例变量。
  • 23. 创建对象的方法之一在定义类时同时创建对象,例如: class date { int month,day,year; public: void setdate(int,int,int); void print( ); int getyear( ); int getmonth( ); int getday( ); } tt; // 同时创建对象tt。
  • 24. 创建对象的方法之二在定义类后在创建对象,例如: Data date1,date2;
  • 25. 对象的使用 对象的性质和定义要求将实例变量隐藏在对象中,对它们的访问,原则上要通过其接口——共有的成员函数来进行。访问对象的成员时,使用“.”运算符: 对象名.成员名称; 调用成员函数是必须指明对象,事实上,在C++语言中,我们用OOP术语发送消息来代替“调用成员函数”这个说法。
  • 26. 2.1.5  C++中类的接口与实现一般把仅含函数原型的类声明部分称为类的接口(interface),例如: class Date{ int day, month, year; public: void init (int dd,int mm,int yy); //initialize void add_year(int n); // add n years void add_month(int n); // add n months void add_day(int n); // add n days }
  • 27. C++中类的接口与实现(续)把类中成员函数的定义部分称为类的实现部分(implementation)。例如: void Date::init(int dd, int mm, int yy) { day = dd; month = mm; year = yy; } inline void Date::add_year(int n) { year += n; }
  • 28. C++中类的接口与实现(续)C++中的类实现了接口和实现的分离,这样只要维持接口不变,实现部分可以根据需要不断改进,而不影响类的使用。 类的接口部分一般存放在扩展名为h的头文件中,类的实现部分则存放在扩展名为cpp的实现文件中。在cpp文件的开头要用include将h头文件包含在其中。 这个特征增强了类模块的独立性和可重用性(reuse)。
  • 29. 设计一个类(class)时的基本原则在设计一个新的类(class)类型时的基本原则是将类的复杂的实现细节(例如用来存储该类对象的数据)和正确使用该类所需知道的要素(例如访问对象数据的函数)分离开来。 这种分离的最好方式就是通过一个特定的接口来使用该类的数据结构和内部处理程序。
  • 30. 封装(encapsulation)在进行类定义时,我们把需要提供给类的用户的在类对象上执行的操作声明为公用的(public),这样就形成了类的接口。 然后按照信息隐藏(information hiding )的原则来把类的内部数据表示和实现细节声明为私有的(private),从而完成了封装(encapsulation)。
  • 31. 封装性及其好处所谓封装,在面向对象程序设计中的含义就是包含和隐藏对象信息,如内部数据结构和代码的能力。 在C++中封装是通过类来实现的,即将内部的数据和代码声明为私有的,外界不能直接访问。 封装将操作对象的内部复杂性与应用程序的其他部分隔离开来。 换句话说,应用类对象的程序员不需要知道类内部的实现细节就可以使用该类。这是面向对象程序设计降低复杂性的最主要途径。
  • 32. 2.1.6  类声明与类定义类定义包含两部分:类头和类体,类体由一对花括号包围起来。一旦到了类体的结尾,即结束右边的花括号,这时就定义了一个类 。 一旦定义了一个类,则该类的所有成员就都是已知的,类的大小也是已知的了。
  • 33. 类声明由于历史的原因,类的定义(class definition)也称为类的声明(declaration)。 但是准确地说,一旦给出了类头,就声明了一个类。我们可以声明一个类但是不定义它。 例如: class Date;// Date类的声明 这个声明向程序引入了一个名字Date,指示Date为一个类类型。
  • 34. 类声明(续)如果还没有定义类,那么我们就不能定义这类类型的对象 但是,我们可以声明指向这种类型对象的指针或者引用。 class Date; // Date类的声明 Date pd, &rd; // 声明了指向Date类对象的指针pd和引用rd
  • 35. 2.1.7  结构struct与类class的比较结构和类的唯一区别是: 在缺省情况下结构内数据成员和成员函数是公有的public,而类中的数据成员和成员函数是私有的private。 可以说C++中的结构是一种特殊的类。 类(或者结构)的私有成员只能被该类(或者结构)的成员函数访问,这是C++实现封装的一种方法。
  • 36. §2.2 构造函数和析构函数2.2.1  构造函数 2.2.2  析构函数 2.2.3  重载构造函数 2.2.4  组合类和对象成员的初始化
  • 37. 2.2.1  构造函数 constructor构造函数是类的一种特殊成员函数,用来为对象进行初始化 。 构造函数的函数名与类名相同。
  • 38. 构造函数的例子1class Date { int day, month, year; Date(int dd, int mm, int yy) ; / / constructor }; //构造函数名与类名相同 void Date::Date(int dd, int mm, int yy) { day = dd; month = mm; year = yy; }用来为对象进行初始化 。
  • 39. 构造函数的例子2class Student{ private: int number; char name[15]; float score; public: Student(int number1,char *name1,float score1); void modify(float score1) { score=score1; } void print( ); };构造函数的声明
  • 40. 构造函数的例子2//构造函数的定义 Student::Student (int number1,char *name1,float score1) { number=number1; strcpy(name,name1); score=score1; }
  • 41. 构造函数(续)构造函数可以有任一类型的参数,但不能具有返回类型。 当定义一个对象时,系统自动调用构造函数进行初始化。 构造函数与其它函数一样可以重载也可以有缺省参数。 在对象生成的时候自动执行初始化,这会消除任何错误地不执行初始化的可能。 这是C++面向对象程序设计帮助减少复杂性的另一途径。
  • 42. 有缺省参数的构造函数class complex{ private: double real; double image; public: void complex(double r=0.0,double i=0.0) { real=r; image=i; } double realcomplex() { return real; } …; };缺省参数
  • 43. 2.2.2  析构函数析构函数的作用与构造函数正好相反,当对象被删除时,利用析构函数进行一些善后处理。 一般情况下析构函数执行构造函数的逆操作,例如可以利用析构函数来释放构造函数所动态申请的内存空间。 析构函数的名称与其类名称相同,并在名称的前边加~(键盘上的波浪符号)符号。 一个类中只能有一个析构函数。(不能重载) 析构函数不允许有返回值,并且析构函数不允许带参数。参见【例2.7】 。
  • 44. 构造函数和析构函数的例子//构造函数的定义 Student::Student (int number1,char *name1,float score1) { number=number1; name=new char[strlen(name1)+1]; //申请动态内存单元 strcpy(name,name1); score=score1; } //析构函数的定义 Student::~Student( ) { delete [ ] name; //释放动态内存单元 }
  • 45. 2.2.3  重载构造函数构造函数与其它函数一样可以重载也可以有缺省参数。 要重载类的构造函数,只需要在类中声明构造函数的不同函数原型并且定义相关的函数体。
  • 46. 重载构造函数主要有三个原因 1. 为了获得灵活性:提供给用户多种初始化对象的选项。 2. 为了支持数组:要声明一个类的对象数组则该类应具有一个不带参数的构造函数。 3. 为了自定义复制构造函数,将对象进行准确的复制(拷贝):参见第5章堆与复制构造函数。
  • 47. 重载构造函数例一class Date { int d, m, y; public: / / ... Date(int, int, int) ; / / day, month, year Date(int, int) ; / / day, month, today’s year Date(int) ; / / day, today’s month and year Date() ; / / default Date: today Date(const char*) ; / / date in string representation };为了获得灵活性:提供给用户多种初始化对象的选项
  • 48. 2.2.4  组合类和对象成员的初始化当一个类的对象作为一个类的成员时,称为该类的对象成员。 使用对象成员着重要注意的问题是一个类的内部对象的初始化问题。
  • 49. 类的对象成员初始化#include "iostream.h" class inner_class{ int x; public: inner_class(int z){x=z;} void write(){ cout<
  • 50. 类的对象成员初始化(续)class outer_class{ int y; inner_class x; inner_class r; public: outer_class(int z); void write(){cout<
  • 51. 类的对象成员初始化(续)outer_class::outer_class(int z):x(20),r(-36) { y=z;} main() { outer_class obj(10); obj.write_inner_x(); obj.write_inner_r(); obj.write(); return 0; }初始化参数传递链
  • 52. 初始化和撤销的顺序先里后外:先调用对象成员的构造函数,再调用外围类的成员函数。 虽然对象成员调用它们类的构造函数,但此时写的是对象的名字,而不是类名。 先外后里:当撤销一个包含对象成员的对象时,该对象的析构函数将先执行,然后再调用对象成员的析构函数。
  • 53. 【例2.9】对象成员的初始化。 //注意:在定义Student类的构造函数时,必须缀上对象成员的名字birthday Student::Student(int number1,char *name1,float score1, int d1,int m1,int y1):birthday(d1,m1,y1) //对象成员birthday的初始化 { number=number1; strcpy(name,name1); score=score1; }
  • 54. 2.3 类与const软件工程的历史经验表明,程序内部相当多的隐蔽错误是由于无意中修改了某些数据的值造成的。 对数据进行保护是减少程序中的错误,提高软件的可靠性的有效途径之一。 在C++中还广泛使用const关键字,用来实现对共享数据的保护,提高软件的安全性。
  • 55. 2.3.1  常成员函数 Constant Member Functionsclass Date { int d, m, y; public: int day( ) const { return d; } int month( ) const { return m; } int year( ) const; / / ... };使用const关键字说明的函数常成员函数不更新对象的数据成员。(只读函数)
  • 56. 常成员函数(续)inline int Date::year( ) const / / 正确 { return y; } inline int Date::year( ) / / 错误 { return y; }const是函数类型的一个组成部分,因此在实现部分也要带const关键字。遗漏了const关键字
  • 57. 常成员函数(续)inline int Date::year( ) const { return y++; } 将出现编译错误信息:企图在常成员函数中修改对象的数据成员。
  • 58. 2.3.2  常对象constant object 常对象是指对象常量,定义格式如下:   <类名> const <对象名> 或者   const <类名> <对象名> 例如: const Date cd;
  • 59. 常对象 constant object (续)必须对常对象进行初始化,而且不能被更新。 常对象只能调用它的常成员函数。 普通的对象既可以调用它的常成员函数,也可以调用普通成员函数。 请看下面的例子:
  • 60. 常对象 constant object (续)void f(Date& d, const Date& cd) { int i = d.year( ) ; / / ok d.add_ year(1) ; / / ok int j = cd.year( ) ; / / ok cd.add_ year(1) ; / / 错误 }常对象不能调用它的普通成员函数
  • 61. 常类型 cosnt 常类型的变量或对象必须进行初始化,而且不能被更新。 常数组:数组元素不能被更新。 类型说明符 const 数组名[大小]... 常引用:被引用的对象不能被更新。 const 类型说明符 &引用名 常指针:指向常量的指针(1.3.2小节介绍)。 C++中const关键字使用得很广泛,主要目的是对共享数据的保护,提高软件的安全性。