630 likes | 737 Views
程序设计是计算机学科的. 核心和灵魂. 程序设计基础. 第五章 类与对象. 类的作用 类界面与类实现 类成员的访问控制 对象的创建 静态数据成员 构造函数与析构函数 成员对象的初始化. §5.1 类的引入. 实体:从属性与行为两个方面刻画一个实体 现实世界实体例:银行账户、学生、传感器、 … 思维世界实体例:复数、矩阵、堆栈、 … 类:描述实体的抽象概念 对象:对应于一个个具体实体. §5.1 类的引入. 5.1.1 循环计数器 循环计数: 0 0 ~359 0 、 1~12
E N D
程序设计是计算机学科的 核心和灵魂 程序设计基础 第五章 类与对象
类的作用 • 类界面与类实现 • 类成员的访问控制 • 对象的创建 • 静态数据成员 • 构造函数与析构函数 • 成员对象的初始化
§5.1 类的引入 实体:从属性与行为两个方面刻画一个实体 • 现实世界实体例:银行账户、学生、传感器、… • 思维世界实体例:复数、矩阵、堆栈、… 类:描述实体的抽象概念 对象:对应于一个个具体实体
§5.1 类的引入 5.1.1 循环计数器 循环计数:00~3590、1~12 数据定义:min_val、max_val、current 函数设计:increment()、decrement()、 set_mode()、 get_value()、set_value
§5.1 类的引入 5.1.1 循环计数器 保护内部数据: static int min_val; static int max_val; static int current; 信息隐藏 • 将一个模块的内部数据或操作隐藏起来,其他模块只能通过该模块提供的公开操作来访问该模块 • 信息隐藏由封装机制来实现。 在C语言中用文件作为模块来实现
§5.1 类的引入 5.1.1 循环计数器 用文件作为模块来实现封装 CIRNUM1.Hpp //循环计数器头文件 CIRNUM1.CPP //实现循环计数器
§5.1 类的引入 增加程序可靠性和可重用性 5.1.2 循环计数器的讨论 用文件作为循环计数器模块的好处: 头文件作为模块界面,保护内部数据; 应用程序不关心模块内部实现细节; 便于模块设计者修改实现方法和扩展新功能。 不足: 不够抽象,如定义三个记录角度、月份和时间的计数器; 函数名冲突问题; 模块修改必须重新编译。
§5.1 类的引入 5.1.3 类作为构造程序的基本单位 类(class):构造程序的基本单位,将数据以及这些数据上的操作(函数)封装在一起。 类成员(member):数据与函数 C++类的作用: 数据封装 类型定义
§5.2 类的定义 5.2.1 类声明 类是对一群具有相同属性、表现相同行为的对象的描述,类声明的一般形式 : class 类名 { public: 公有数据和函数 private: 私有数据和函数 }; 类中定义的数据(变量)称为数据成员类中定义的函数(行为操作)称为成员函数
§5.2 类的定义 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;// 当前值 }; 5.2.1 类声明 例:循环计数器类 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; }
§5.2 类的定义 5.2.1 类声明 说明: • 类的数据成员允许是任何数据类型,但不能是本类类型的。正在定义的类名只可以以指针形式声明自己类中的数据成员 class Link { class Link { Link member; Link *member; ………… }; };
= 0 = 359 §5.2 类的定义 5.2.1 类声明 • 类中的数据成员不允许使用初值表达式 class CIRCULAR_NUMBERS { … private: int min_val ; int max_val ; … }; • 类中的数据成员不允许使用auto,register,extern修饰,但允许使用static修饰,得到静态数据成员:供该类的所有对象公用
§5.2 类的定义 5.2.1 类声明 • 在类中 const 的使用 • const修饰数据成员:用于声明类中的一个常量数据成员。由于类的数据成员在声明时不能有初值表达式,因此类的常量数据成员在声明时不能指定常量值。 class C { const double PI ; …… }; • const修饰成员函数:常量成员函数,常量成员函数中不允许修改本类数据成员的值。 class C { int get_value() const { …… } };
§5.2 类的定义 5.2.2 类成员的访问控制 指明成员的可访问性 • 私有段:由private标记的段 段内的数据成员和成员函数称为私有成员,仅能由该类中的成员函数来访问,即仅可在成员函数定义中使用私有成员的名字。 • 公有段:由public标记的段 段内的数据成员和成员函数称为公有成员,为该类提供与外部世界的接口界面,即可在类内也可在类外访问。 • 类中每一个成员都有访问控制属性,若没有明确指明,成员的访问控制方式缺省为private。
§5.2 类的定义 5.2.3 类界面与类实现 • 类的成员函数可以在类内定义,也可以在类外定义。 • 类界面:头文件 *.hpp声明类,包括类的所有数据成员,以及成员函数的原型 //程序:CIRNUM3.hpp //功能:循环计数器头文件 class CIRCULAR_NUMBERS { public: void set_mode(int min, int max);// 设置循环计数器的上、下限 void set_value(int value) // 设置循环计数器的当前值 int get_value() // 查询循环计数器的当前值 void increment() // 循环计数器加1 void decrement() // 循环计数器减1 private: int min_val; // 最小值 int max_val; // 最大值 int current; // 当前值 };
§5.2 类的定义 5.2.3 类界面与类实现 • 类实现:源程序 *.cpp首先要包含类界面:#include “*.hpp”然后定义成员函数,由于此时成员函数的定义是位于类的外面,因此必须用类名和作用域运算符::来修饰函数名。一般形式为: 成员函数类型 类名::成员函数名(形参表) { 函数体 } 例:// 循环计数器加一 void CIRCULAR_NUMBERS::increment() { int mode = max_val - min_val + 1; current = ((current - min_val) + 1) % mode + min_val; return; }
§5.2 类的定义 5.2.4标识符的类作用域 • { }内即为类作用域 • 在类作用域中声明的标识符只在该类中具有可见性 • 成员名限定:类中一个成员名字可以使用类名和作用域运算符来显式指定 class C { void C::set(int val) public: { value = val; void set(int val); return; private: } int value; }; C::value
§5.3 对象的创建 5.3.1 对象声明 • 类:数据类型 • 具有类类型的变量称为对象,对象称为类的实例。 • 对象声明一般格式: <类名> <对象名> 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 set_value() decrement() increment() get_value() set_mode() min_val current max_val 250 359 0 12 max_val 11 current set_mode() set_mode()代码 set_value() get_value() increment() set_value()代码 increment()代码 decrement() decrement()代码 get_value()代码 §5.3 对象的创建 类CIRCULAR_NUMBERS 的实例 5.3.1 对象声明 CIRCULAR_NUMBERS angle,month;
§5.3 对象的创建 5.3.1 对象声明 静态数据成员 • 用static修饰的数据成员:被分配占用一个固定的存储空间,由此类的所有对象共享,对于类来说:静态数据成员相当于类的全局变量。 • 使用方法: • 在类的界面中(定义)声明一个静态数据成员 • 在类实现中,必须再次声明并初始化这个静态数据变量 // test1.hpp// test1.cpp class C { #include “test1.hpp” private: int x; int C::s = 18 ; int 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 ()代码 §5.3 对象的创建 5.3.1 对象声明 静态数据成员 // 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
§5.3 对象的创建 5.3.2 使用对象成员 通过成员选择运算符.来访问/使用对象的公有成员 对象名.成员名 angle.get_value(); angle.setmode(0,359);
§5.3 对象的创建 5.3.3 对象的生存期 对象生存期:对象从被创建开始到被释放为止的时间。 数据成员生存期有对象生存期决定,成员函数具有全局生存期 (1)局部对象:定义在函数体内或程序块内,作用域限定在函数体或程序块中。 (2)静态对象:定义在一个文件中,作用域从定义时起到文件结束。 (3)全局对象:定义在某个文件中,作用域在包含该文件的整个程序中。
§5.4 对象的初始化 5.4.1 构造函数 • 类中的一个特殊函数 • 功能:对该类的对象进行初始化 • 一般形式:类名(形式参数表); • 特点: • 构造函数是公有的,可由用户提供,也可由系统提供缺省的。 • 构造函数可以有任何类型的形参,但是无返回值,也不能指定返回类型。 • 构造函数在声明一个新对象时调用(对象声明语句中为构造函数提供实参),不可显式调用
§5.4 对象的初始化 5.4.1 构造函数 程序5.4.1 //程序:CIRNUM4.hpp //功能:循环计数器类头文件,使用构造函数 class CIRCULAR_NUMBERS { public: CIRCULAR_NUMBERS(int min, int max, int value); // 设置循环计数器的上下限和当前值 int get_value(); // 查询循环计数器的当前值 void increment(); // 循环计数器加1 void decrement(); // 循环计数器减1 private: int min_val; // 最小值 int max_val; // 最大值 int current; // 当前值 };
§5.4 对象的初始化 5.4.1 构造函数 程序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; } ……
§5.4 对象的初始化 5.4.1 构造函数 程序5.4.3 …… int main() { // 创建两个循环计数器对象并初始化 CIRCULAR_NUMBERS angle(0, 359, 250); // 角度循环计数器 CIRCULAR_NUMBERS month(1, 12, 11); // 月份循环计数器 …… }
§5.4 对象的初始化 全局对象与静态对象的构造函数在main()函数执行之前被调用 5.4.1 构造函数 程序5.4.4 //程序:CIRDEMO5.cpp //功能:演示全局对象与静态对象的初始化 #include “cirnum4.hpp” #include <iostream.h> CIRCULAR_NUMBERS angle(0, 359, 250); // 角度循环计数器作为全局对象 int main() { static CIRCULAR_NUMBERS month(1, 12, 11); // 月份循环计数器作为静态对象 //显示对象状态 cout<<“The initial angle is ”<<angle.get_value()<<“\n”; cout<<“The initial month is ”<<month.get_value()<<“\n”; }
§5.4 对象的初始化 5.4.1 构造函数 程序5.4.5 //程序:一个时间类DATE的例子 //功能:演示多个不同的构造函数 class Date { protected: int year; int month; int day; public: Date() //构造函数,不带参数 { year=0; month=1; day=1; } Date(int y, int m, int d) //构造函数,带参数 { year=y; month=m; day=d; } Date(Date& obj) //构造函数,以一个同类对象为参数 { year=obj.year; month=obj.month; day=obj.day; } void print() { cout<<year<<“/”<<month<<“/”<<day<<endl; } }
§5.4 对象的初始化 5.4.2 析构函数 • 类中的一个特殊函数 • 功能:为撤销一个类的对象做善后清理工作(如:回收存储空间) • 一般形式:~类名( ); • 特点: • 析构函数是公有的,可由用户提供,也可由系统提供缺省的。 • 析构函数不可带任何参数,无返回值,也不能指定返回类型。 • 在对象生存期结束时,程序将自动调用该对象所属类的析构函数,调用且仅调用一次。
§5.4 对象的初始化 5.4.2 析构函数 程序5.4.6 //程序:DESTROY.CPP //功能:演示构造函数与析构函数的调用 #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; }
§5.4 对象的初始化 5.4.2 析构函数 int main() { DEMO_CLASS obj(30);// 声明一个对象 cout << "This is the end of main().\n"; return 0; } 运行结果: Initial value is 30 This is the end of main(). Goodbye!
§5.4 对象的初始化 5.4.3 对象成员的初始化 • 对象成员:在一个类中声明具有类类型的数据成员 • 初始化对象成员的方法:在本类的构造函数中调用这些对象成员所属类的构造函数,其一般形式:类名::类名(形参表):初始化列表{ …… } class A { …… public: B::B():obj2(10,20), obj1(5,3) A(int a, float b); { …… }; } class B { A obj1, obj2; public: B(); }
§5.4 对象的初始化 5.4.3 对象成员的初始化 说明 • 如果有多个对象成员,对象成员构造函数的调用次序取决于这些成员在类中声明的次序。 • 执行次序: 创建类的对象时,调用此类的构造函数,先根据初始化列表调用对象成员的构造函数,初始化对象成员,然后才执行本身的构造函数的函数体。 析构函数调用的次序相反。
§5.4 对象的初始化 5.4.3 对象成员的初始化 程序5.4.8 //程序:OBJECT.CPP //功能:演示类中包含有另一个类的对象初始化 // 在构造函数和析构函数中加入输出以便跟踪 #include<iostream.h> class Object { int val; public: Object( ); //构造函数 Object( int i ); //构造函数 ~Object( ); //析构函数 }; Object::Object( ) { val=0; cout<<"\n Default constructor for Object.\n"; }
§5.4 对象的初始化 5.4.3 对象成员的初始化 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( ); //析构函数 };
§5.4 对象的初始化 5.4.3 对象成员的初始化 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"; }
§5.4 对象的初始化 5.4.3 对象成员的初始化 执行结果为: ... 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的析构函数
§5.4 对象的初始化 5.4.3 对象成员的初始化 初始化列表的其他用途 • 基本数据类型的数据成员也可用初始化列表做初始化 class C { public: C(int i):number(i) C(int i) {……} <=> { number = i; private: …… int number; } };
§5.4 对象的初始化 5.4.3 对象成员的初始化 初始化列表的其他用途 • 常量数据成员的初始化 常量数据成员: 不能在类的声明中初始化(类中不能用初值表达式) 不能在构造函数中设置其值(赋值操作不能用于常量) 只能采用初始化列表中来赋值 例:class C { C::C(int i): PI(3.14159) public: { C(int i); …… private: } const double PI ; };
§5.4 对象的初始化 5.4.3 对象成员的初始化 //程序:OBJECT1.CPP //功能:演示类中包含有另一个类的对象初始化 #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"; } };
§5.4 对象的初始化 5.4.3 对象成员的初始化 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
§5.5 使用类与对象构造程序的实例 5.5.1 模拟数字式时钟 //程序:CLOCK.HPP //功能:时钟类的头文件 #include "cirnum4.hpp" #include <iostream.h> class Clock { public: Clock(int hh, int mm, int ss); void update(); void show(); private: CIRCULAR_NUMBERS hour; CIRCULAR_NUMBERS minute; CIRCULAR_NUMBERS second; };
§5.5 使用类与对象构造程序的实例 5.5.1 模拟数字式时钟 //设置时钟当前值 Clock::Clock(int hh, int mm, int ss): hour(0,23,hh), minute(0,59,mm), second(0,59,ss) { return; } void Clock::update() // 刷新时间 { second.increment(); if(second.get_value()==0) { minute.increment(); if(minute.get_value()==0)hour.increment(); } return; } void Clock::show() //显示时间 { cout<<hour.get_value()<<":"<<minute.get_value()<<":"<<second.get_value()<<endl; return; }
§5.5 使用类与对象构造程序的实例 5.5.1 模拟数字式时钟 //程序:TIMEDEMO.cpp //功能:使用类Clock模拟数字式时钟 #include "clock.hpp" #include <iostream.h> int main() { int loop; Clock rolex(4,15,30); Clock cima(14,0,0); cout<<"Rolex: \n"; //显示rolex对象 for(loop=1; loop<=100; loop=loop+1) { rolex.update(); rolex.show(); } cout<<"Cima: \n"; //显示Cima对象 for(loop=1; loop<=100; loop=loop+1) { cima.update(); cima.show(); } return 0; }
§5.5 使用类与对象构造程序的实例 5.5.2 模拟加油站油泵的对象工作 //程序:PUMP.CPP //功能:加油站油泵实现文件 #include <iostream.h> #include <iomanip.h> const float AMTINT=300.0; // 初始化油桶中的油量 const float PRICE=1.25; // 单价 class Pump { protected: float amtint,price; public: Pump(float,float); void values(); void request(float); };
§5.5 使用类与对象构造程序的实例 5.5.2 模拟加油站油泵的对象工作 Pump::Pump(float start, float todays) { amtint=start; price=todays;} void Pump::values() { cout<<"The gas tank has: "<<amtint<<endl; cout<<"The price per kg of gas is: "<<price<<endl; } void Pump::request(float pumpamt) { float pumped; if(amtint>=pumpamt) pumped=pumpamt; //满足 else pumped=amtint; amtint=amtint-pumped; // 加油后剩余数 cout<<pumpamt<<" kg were requested"<<endl; cout<<pumped<<" kg were pumped"<<endl; cout<<amtint<<" kg remain in the tank"<<endl; cout<<" The total price is: "<<(pumped*price)<<endl; }
§5.5 使用类与对象构造程序的实例 5.5.2 模拟加油站油泵的对象工作 void main() { Pump obj(AMTINT,PRICE); obj.values(); cout<<endl; obj.request(30.0); cout<<endl; obj.request(280.0); } 程序运行结果: The gas tank has: 300 The price per kg of gas is: 1.25 30 kg were requested 30 kg were pumped 270 kg remain in the tank The total price is: 37.5 280 kg were requested 270 kg were pumped 0 kg remain in the tank The total price is: 337.5
§5.5 使用类与对象构造程序的实例 5.5.3 单实例对象 //程序:SINGLE.HPP //功能:单实例类的头文件 class Single_Instance { public: Single_Instance(); ~Single_Instance(); private: static int instance_count; // 当前对象实例计数 }; #include <iostream.h> int Single_Instance::instance_count=0; // 初始化实例计数,注意静态成员的这种用法 Single_Instance::Single_Instance() //构造函数中必须为实例计数加1 { instance_count++; if(instance_count>1) cout<<"Warning: more than one object instance!"<<endl; } Single_Instance::~Single_Instance() //析构函数必须为实例计数减 1 { instance_count--; }
§5.5 使用类与对象构造程序的实例 5.5.3 单实例对象 //程序:SGL_DEMO.HPP //功能:演示单实例对象的用法 #include "single.hpp" #include <iostream.h> int main() { cout<<" I have the first object."<<endl; Single_Instance obj1; cout<<" I have the second object."<<endl; Single_Instance obj2; cout<<" End of demostration"<<endl; return 0; } 程序运行结果: I have the first object. I have the second object. Warning: more than one object instance! End of demostration