370 likes | 531 Views
第五章 类与对象. 类的作用 类界面与类实现 类成员的访问控制 对象的创建. 静态数据成员 构造函数与析构函数 成员对象的初始化. §5.1 面向对象的基本概念和特征. 1. 对象( Object ) 指将数据 ( 内部状态 ) 和使用这些数据的一组基本操作 ( 行为 ) 封装在一起的统一体。 对象有时指一个事物,称 实例 (instance) 有时指一组类似的事物,称 对象类 (class) 2. 信息隐藏 将一个模块的内部数据或操作隐藏起来,其他模块只能通过该模块提供的公开操作来访问该模块 信息隐藏由封装机制来实现。
E N D
第五章 类与对象 类的作用 类界面与类实现 类成员的访问控制 对象的创建 • 静态数据成员 • 构造函数与析构函数 • 成员对象的初始化
§5.1 面向对象的基本概念和特征 1. 对象(Object) • 指将数据(内部状态)和使用这些数据的一组基本操作(行为)封装在一起的统一体。 • 对象有时指一个事物,称实例(instance)有时指一组类似的事物,称对象类(class) 2. 信息隐藏 • 将一个模块的内部数据或操作隐藏起来,其他模块只能通过该模块提供的公开操作来访问该模块 • 信息隐藏由封装机制来实现。 3.封装: 用两种方法实现
用两种方法实现封装 (1) 文件模块 • 例子:P129 采用文件模块来封装,实现循环计数器功能 • 此程序共包括2个源程序以及1个头文件 • CIRNUM1.HPP (程序5.1.1 ) 头文件仅包含循环计数器对外公开的函数原型,不涉及内部数据与函数定义; • CIRNUM1.CPP (程序5.1.2 )该源程序是循环计数器的实现文件,以.CPP作为后缀名字,包括了内部数据及函数定义。文件中引用CIRNUM1.HPP :#include "cirnum1.hpp" • CIRDEMO1.CPP (程序5.1.3 )该源程序包括主函数,使用循环计算器
基类 派生类 (2) 类:用类实现封装 • C++语言通过建立一个合适的循环计数器类,将这些数据与函数结合在一起,形成一个新的数据类型,称为类类型。 4. 类与类的层次结构 • 具有一致数据结构(属性)和行为(操作)的对象可归并为一个类 • 层次结构: • 上层称超类(基类、父类) • 下层称子类(派生类)
5.类的特征 • 继承性 • 具有层次关系的类的属性和操作进行共享的方式,后代类可以继承祖先类的各种性质 • 多态性 • 同一名字,多种语义;或相同界面,多种实现 • 如:一个函数名代表了多种函数的实现
§5.2 类的定义 一、类的声明 • 类是对一群具有相同属性、表现相同行为的对象的描述 • 类的一般形式 : class 类名 { public: 公有数据和函数 protected: 保护数据和函数 private: 私有数据和函数 }; • 类中定义的数据(变量)称为数据成员 • 类中定义的函数(行为操作)称为成员函数
例:循环计数器类 class CIRCULAR_NUMBERS { public: void set_mode(int min, int max) // 设置循环计数器的上、下限 { min_val = min; max_val = max; return; } void set_value(int value) // 设置循环计数器的当前值 { current = value; return; } int get_value() //查询循环计数器的当前值 { return current; }
例:循环计数器类 void increment()// 循环计数器加一 { int mode = max_val – min_val + 1; current = ((current – min_val) + 1) % mode + min_val; return; } void decrement()// 循环计数器减一 { int mode = max_val – min_val + 1; current = ((current – min_val) - 1 + mode)% mode + min_val; return; } private: int min_val; // 最小值 int max_val; // 最大值 int current; // 当前值 };
说明 • 类的数据成员允许是任何数据类型,但不能是本类类型的。正在定义的类名只可以以指针形式声明自己类中的数据成员 class Link class Link { Link member; { Link *member; …… …… }; };
说明 • 类中的数据成员不允许使用初值表达式 class CIRCULAR_NUMBERS { … private: int min_val =0 ; int max_val =359 ; … }; • 类中的数据成员不允许使用auto、register、 extern修饰,但允许使用static修饰,得到静态数据成员:供该类的所有对象公用
常数据成员和常成员函数 • const修饰数据成员:用于声明类中的一个常量数据成员。由于类的数据成员在声明时不能有初值表达式,因此类的常量数据成员在声明时不能指定常量值。 class C { const double PI ; …… }; • const修饰成员函数:常量成员函数,常量成员函数中不允许修改本类数据成员的值。 class C { int get_value() const { …… } };
二、类成员的访问控制 指明成员的可访问性 • 私有段:由private标记的段 段内的数据成员和成员函数称为私有成员,仅能由该类中的成员函数来访问,即仅可在成员函数定义中使用私有成员的名字。 • 公有段:由public标记的段 段内的数据成员和成员函数称为公有成员,为该类提供与外部世界的接口界面,即可在类内也可在类外访问。 • 类中每一个成员都有访问控制属性,若没有明确指明,成员的访问控制方式缺省为private。
三、类界面与类实现 • 类的成员函数可以在类内定义,也可以在类外定义。 • 类界面:头文件 *.hpp声明类,包括类的所有数据成员,以及成员函数的原型 • 类实现:源程序 *.cpp首先要包含类界面:#include “*.hpp”然后定义成员函数,由于此时成员函数的定义是位于类的外面,因此必须用类名和作用域运算符::来修饰函数名。 • 一般形式为: 成员函数类型 类名::成员函数名(形参表) { 函数体 }
三、类界面与类实现 例:// 循环计数器加一 class CIRCULAR_NUMBERS {void increment();// 循环计数器加一 void decrement()// 循环计数器减一 { int mode = max_val – min_val + 1; current = ((current – min_val) - 1 + mode)% mode + min_val; return; } private: int min_val; // 最小值 int max_val; // 最大值 int current; // 当前值 }; void CIRCULAR_NUMBERS :: increment( ) { int mode = max_val - min_val + 1; current = ((current - min_val) + 1) % mode + min_val; return; }
四、 标识符的类作用域 • { }内即为类作用域 • 在类作用域中声明的标识符只在该类中具有可见性 • 成员名限定:类中一个成员名字可以使用类名和作用域运算符来显式指定 class C void C::set(int val) { public: { value = val; void set(int val); return; private: } int value; }; C::value
§5.3 对象的创建 • 类:数据类型 • 具有类类型的变量称为对象,对象称为类的实例。 int main() { CIRCULAR_NUMBERS angle; // 角度循环计数器 CIRCULAR_NUMBERS month; // 月份循环计数器 angle.set_mode(0, 359); // 设置角度取值范围从0到359 angle.set_value(250); // 设置当前角度为250 month.set_mode(1, 12); // 设置月份取值范围从1到12 month.set_value(11); // 设置当前月为11月 }
类 CIRCULAR_NUMBER 对象month 对象angle 1 min_val get_value() decrement() increment() set_value() current set_mode() min_val max_val 359 0 250 12 max_val 11 current set_mode() set_mode()代码 set_value() get_value() increment() set_value()代码 increment()代码 decrement() get_value()代码 decrement()代码 §5.3 对象的创建
一、静态数据成员 • 用static修饰的数据成员:被分配占用一个固定的存储空间,由此类的所有对象共享,对于类来说:静态数据成员相当于类的全局变量。 • 使用方法: • 在类的界面中(定义)声明一个静态数据成员 • 在类实现中,必须再次声明并初始化这个静态数据变量
一、静态数据成员 // test1.hpp // test1.cppclass C #include “test1.hpp”{ private: int C::s = 18 ; int x, y;static int s; int C::set(int i, int j) public: { x = i ; int set(int,int); y = j ; }; s++; }
set() x y 对象c2 对象c1 x y s: 18 set() set ()代码 一、静态数据成员 // test.cpp #include “test1.hpp” void main( ) { C c1, c2; c1.set(100,200); c2.set(15, 30); } 类C 19 20 100 200 15 30
二、使用对象成员 • 通过成员选择运算符“.”来访问/使用对象的公有成员 • 对象名.数据成员 • 对象名.成员函数名(实参列表) class C { public: int x,y; void setxy(int a,int b) { x=a; y=b;} }; void main() { int x,y; C: z; z.setxy(3,5); cout<<“x=“<<x<<endl; cout<<“z.x=“<<z.x<<endl; }
§5.4 对象的初始化 一、构造函数 二、析构函数 三、对象成员的初始化
一、构造函数 • 构造函数是类中的一个特殊函数 • 功能:对该类的对象进行初始化 • 一般形式:类名(形式参数表); • 特点: • 构造函数是公有的,可由用户提供,也可由系统提供缺省的。 • 构造函数可以有任何类型的形参,但是无返回值,也不能指定返回类型。 • 构造函数在声明一个新对象时调用(对象声明语句中为构造函数提供实参),不可显式调用
例: class CIRCULAR_NUMBERS { private: int max, min, value; public: CIRCULAR_NUMBERS(int min, int max, int value); // 设置循环计数器的上下限和当前值 …… }; 程序5.4.2 … // 设置循环计数器的上下限和当前值 CIRCULAR_NUMBERS::CIRCULAR_NUMBERS(int min, int max, int value) { min_val = (min <= max) ? min : max; max_val = (min <= max) ? max : min; if (value < min_val) current = min_val; else { if (value > max_val) current = max_val; else current = value; } return; } ……
…… int main() { // 创建两个循环计数器对象并初始化 CIRCULAR_NUMBERS angle(0, 359, 250); // 角度循环计数器 CIRCULAR_NUMBERS month(1, 12, 11); // 月份循环计数器 …… }
二、析构函数 • 析构函数也类中的一个特殊函数 • 功能:为撤销一个类的对象做善后清理工作(如:回收对象所占用存储空间) • 一般形式:~类名( ); • 特点: • 析构函数是公有的,可由用户提供,也可由系统提供缺省的。 • 析构函数不可带任何参数,无返回值,也不能指定返回类型。 • 在对象生存期结束时,程序将自动调用该对象所属类的析构函数,调用且仅调用一次。
运行结果是: Initial value is 30 This is the end of main(). Goodbye! 例: #include <iostream.h> class DEMO_CLASS { public: DEMO_CLASS(int i); ~DEMO_CLASS(); }; DEMO_CLASS::DEMO_CLASS(int i) { cout << "Initial value is " << i << "\n"; return; } DEMO_CLASS::~DEMO_CLASS() { cout << "Goodbye!\n"; return; } int main() { DEMO_CLASS obj(30);// 声明一个对象 cout << "This is the end of main().\n"; return 0; }
三、对象成员的初始化 • 对象成员:在一个类中声明具有类类型的数据成员 • 初始化对象成员的方法:在本类的构造函数中调用这些对象成员所属类的构造函数,其一般形式:类名::类名(形参表):初始化列表{ …… } class A …… { public: B::B():obj2(10,20), obj1(5,3) A(int a, float b); { …… }; } class B { A obj1, obj2; public: B(); }
说明 • 如果有多个对象成员,对象成员构造函数的调用次序取决于这些成员在类中声明的次序。 • 执行次序: • 创建类的对象时,调用此类的构造函数,先根据初始化列表调用对象成员的构造函数,初始化对象成员,然后才执行本身的构造函数的函数体。 • 析构函数调用的次序正好相反。
类中包含有另一个类的对象的例子 //在构造函数和析构函数中加入输出以便跟踪 #include<iostream.h> class Object { int val; public: Object( ); //构造函数 Object( int i ); //构造函数 ~Object( ); //析构函数 }; Object::Object( ) { val=0; cout<<"\n Default constructor for Object.\n"; }
Object::Object( int i) //构造函数定义 { val=i; cout<<"\n Constructor for Object: "<<val<<endl; } Object::~Object( ) //析构函数定义 { cout<<"\n Destructor for Object: "<<val<<endl;} class Container //定义新类,它含Object的对象 { private: int date; Object one; //对象成员 Object two; //对象成员 public: Container( ); //构造函数 Container( int i, int j, int k); //构造函数 ~Container( ); //析构函数 };
Container::Container( ) //构造函数定义 { date=0; cout<<"\n Default constructor for Container.\n"; } Container::Container( int i, int j, int k):two(i),one(j) { date=k; cout<<"\n Constructor for Container.\n"; } Container::~Container( ) { cout<<"\n Destructor for Container.\n"; } void main( ) //演示程序 { cout<<"\n... Entering main ...\n"; Container obj(5,6,10); cout<<"\n... Exiting main ...\n"; }
执行结果为: ... Entering main ... Constructor for Object: 6 //对象one调用构造函数 Constructor for Object: 5 //对象two调用构造函数 Constructor for Container. //调用Container的构造函数 ...Exiting main ... Destructor for Container. //调用Container的析构函数 Destructor for Object: 5 //调用two的析构函数 Destructor for Object: 6 //调用one的析构函数
初始化列表的其他用途 • 基本数据类型的数据成员也可用初始化列表做初始化 class C { public: C(int i):number(i) <=>C(int i) {……}{ number = i; private: …… int number;} };
常量数据成员的初始化 常量数据成员: 不能在类的声明中初始化(类中不能用初值表达式) 不能在构造函数中设置其值(赋值操作不能用于常量) 只能采用初始化列表中来赋值 例:class C C::C(int i): PI(3.14159) { public: { C(int i); …… private: } const double PI ; };
例: #include <iostream.h> #include <conio.h> class base { public: base(char *s) { cout<<"Construct for Base "<<s<<"\n"; } ~base() { cout<<"Destruct for Base\n"; } }; class screen { private: const unsigned size; long price; base obj; public: screen(int s, long p):size(s),price(p),obj("testing") {cout<<"Construct for Screen\n"; } void list(); ~screen() { cout<<"Destruct for Screen\n"; } };
void main() { screen nec(14,40000L); nec.list(); getch(); } void screen::list() { cout<<"The size of the screen is :"<<size<<"\n"; cout<<"The price of the screen is :"<<price<<"\n"; } 运行结果: Construct for Base testing Construct for Screen The size of the screen is :14 The price of the screen is :40000 Destruct for Screen Destruct for Base