第五章类与对象第五章 类与对象
§ 5.1 面向对象方法
§ 5.2 类的定义
§ 5.3 对象的创建
§ 5.4 对象的初始化
§ 5.5 特殊的类成员
§ 5.1 面向对象方法
面向对象的基本思想
面向对象思想的由来与发展
面向对象的基本概念
§ 5.1 面向对象方法面向对象的基本思想
从现实世界中客观存在的事物(对象)出发来构造系统对象,对 问题领域 内人、物、事情等的 抽象
并在系统构造中运用人类的自然思维方式
(抽象、分类、组合、继承、封装)
§ 5.1 面向对象方法
面向对象不仅是一些具体的软件开发技术,它是一整套关于如何看待软件系统与现实世界关系,以何种观点来研究问题并进行求解,以及如何进行系统构造的软件方法学
§ 5.1 面向对象方法
用面向对象思想构造软件的主要内容
① 对象是系统的基本单位
② 对象的属性和操作的有机结合组成一个完整的对象
③ 具有共同特征的对象抽象成类
④ 对类进一步抽象,形成类簇
⑤ 对象之间存在静态关系和动态关系 (组合、
继承、消息等)
§ 5.1 面向对象方法面向对象基本思想 ——抽象
现实世界的事物抽象成对象
现实世界事物之间的关系抽象成软件系统中对象之间的关系
具有共同特征的对象抽象成类
具有共同特征的类抽象成基类和派生类
§ 5.1 面向对象方法面向对象基本思想 ——封装
对象是属性和操作的组合
对象的表现(服务,接口)和实现细节分开
§ 5.1 面向对象方法
面向对象方法的定义利用抽象、封装等机制,借助于对象、类、继承、消息传递等概念进行软件系统构造的软件开发方法
§ 5.1 面向对象方法
面向对象的由来与发展面向对象方法起源于面向对象的编程语言在编程语言得到充分发挥和应用的同时,面向对象方法得到了发展。
反过来又为面向对象编程提供了先期的设计和指导
§ 5.1 面向对象方法
面向对象方法是软件理论的返璞归真
软件开发的发展过程程序员的智慧和技巧面向过程面向数据面向功能面向应用面向数据流面向对象
§ 5.1 面向对象方法面向对象基本概念
对象
消息

类与对象的关系
§ 5.1 面向对象方法对象
对象是对问题域中客观存在的事物的抽象,它是一组属性和在这些属性上的操作的封装体
对象的两大要素:属性和操作
--属性,状态,特征,组成成员
--操作,修改属性,访问属性,执行命令
§ 5.1 面向对象方法
例:“文件”作为一个对象属性,操作:
文件名 —filename 文件复制 ——copy
文件长度 —length 文件更名 ——rename
修改日期 —modify-date 文件打印 ——print
文件内容 —contents ………..
§ 5.1 面向对象方法消息向对象发出的服务请求
是面向对象系统中对象之间交互的途径
要素:发送者、接受者、服务、参数
oopl中的消息是对类的成员函数的调用
对消息的响应取决于接受消息的对象
§ 5.1 面向对象方法类 class
类是具有相同属性和操作的一组对象的集合,它为属于该类的全部对象提供了统一的抽象描述类是对一类对象的抽象类定义了这一类对象所共有的特征和行为人类抽象思维的产物
oopl中的类相当于一种数据类型
§ 5.1 面向对象方法类与对象的关系
类是抽象的,对象是具体的
类是模板,对象是实例
类是类型,对象是变量
类是静态的,对象是动态的
§ 5.1 面向对象方法
类是具有相似特征的对象的抽象
--文件类 所有具体文件对象的抽象
--书 类 所有具体书对象的抽象
--学生类 所有具体学生对象的抽象
§ 5.2 类的定义
类定义的一般形式
内联函数与非内联函数
类成员的访问控制
类作用域
§ 5.2 类的定义
类定义的一般形式
class 类名
{
public:
公有数据和函数
private:
私有数据和函数
};
分号不能丢!
C++合法的标识符,用大写类定义体中的数据和函数分别称为:
数据成员和成员函数
§ 5.2 类的定义
类定义体中包括两种类的成员:
数据成员 和 函数成员
通常类的定义格式:
class 类名
{
public:
成员函数
private:
数据成员
};
§ 5.2 类的定义
例:定义一个“文件”类
class FILE
{
public:
bool copy(FILE &target);
bool rename(char *new_name);
bool print( );
private:
char file_name[100];
int length;
char *contents;
};
操作成员函数属性数据成员
§ 5.2 类的定义
操作的实现(成员函数的定义)
bool FILE::copy(……)
{
……
}
bool FILE::rename(……)
{
……
}
bool FILE::print( )
{
……
}
§ 5.2 类的定义
注:
① 类中说明的数据成员不能初始化
② 类中数据成员的类型可以是 int,char,
float等各种数据类型,还可以是对象
§ 5.2 类的定义
内联函数与非内联函数
⑴ 内联函数
class FILE
{
…….
bool rename(char *new_name)
{
strcpy(file_name,new_name);
}
……
};
成员函数的函数体定义在类体内
§ 5.2 类的定义
⑵ 非内联函数
class FILE
{
…….
bool rename(char *new_name);
…….
};
bool FILE::rename(char *new_name)
{
strcpy(file_name,new_name);
}
注,在类体外定义的成员函数要在函数名前加上类名,以便与普通函数相区别
§ 5.2 类的定义
⑶ 二者比较内联函数的好处:
类的定义集中 (将类的所有成员都放在类定义体中)
运行速度快内联函数的缺点:
类定义的规模太大可读性不好通常,将简单的函数作为内联函数将功能复杂的函数作为非内联函数
§ 5.2 类的定义
类定义可分为 类界面 与 类实现类界面,只包括类的所有数据成员和成员函数原型存放在头文件中(,HPP)
类实现,包括类的所有成员函数的定义存放在源文件中(,CPP)
§ 5.2 类的定义
类的成员的访问控制 (封装在类中的实现)
--公有 (public) --公开
--保护 (protected) --适当公开
--私有 (private) --不公开
§ 5.2 类的定义
封装 (信息隐藏 )
接口 (interface)
实现 (implementation)
§ 5.2 类的定义
封装的好处用户角度:简单只了解接口,不用了解细节只需要结果,不规定实现方式对象角度:保护信息安全(不直接访问数据)
安全审计( auditing)
§ 5.2 类的定义访问控制的基本原则:
类的数据成员一般不公开
--private,protected
表示接口的成员函数一般公开
--public
与实现细节有关的成员函数不公开
-- private,protected
§ 5.2 类的定义注:
默认的类成员访问控制方式为 private
各种访问控制方式在类定义体中的先后顺序无关
通常将对外接口部分 public放在前属性报刊 A
报刊 B
……
钱箱服务报刊零售货款清单顾客举例,用“售报亭”对象描述现实中的一个售报亭消息,顾客对售报亭说:“买一份,每日新报,!”
消息的接受者,某个售报亭要求的服务,报刊零售输入的信息,要买的报刊种类,份数和递进去的钱返回的信息,买到的报纸和找回的零钱售报亭
§ 5.2 类的定义类作用域
类的作用域 包括:
类的定义体 { };
类的所有非内联函数的定义体
类成员的作用域 是类作用域
类对象的作用域 遵循一般变量的作用域规则
§ 5.3 对象的创建
直接创建对象
使用对象成员
对象的作用域与生存期
举例
§ 5.3 对象的创建
创建对象有两种方式:
直接创建动态创建(用 new运算符创建)
直接创建对象格式,<类名 > <对象名表 >
例如,FILE f1,f2; int x,y;
§ 5.3 对象的创建
使用对象成员格式:
<对象名 >,<数据成员名 >
或 <对象名 >,<函数成员名 >(参数表 )
其中‘,’称为成员选择运算符
§ 5.3 对象的创建
访问成员变量
strcpy(f1.file_name,“oopd.dat”);
f2.length+=10;
访问成员函数
f1.copy(f2);
f1.rename(“class A”);
注,只有 public的成员才能调用或访问
§ 5.3 对象的创建对象的作用域与生存期
遵循一般变量的作用域、生存期规则
例:
FILE my_file;
void main( )
{
FILE your_file;
my_file.copy(your_file);
your_file.rename(“newname”);
}
全局对象局部对象
§ 5.3 对象的创建
域操作符,:
①,:identifier 文件域标识符
② classname::identifier 类域标识符
③ identifier 当前域标识符
bool FILE::rename(char file_name)
{
strcpy(FILE::filename,file_name);
}
举例,定义一个日期类,该类的对象是某个具体日期
//日期类的界面
class DATE
{
public:
void setdate(int y,int m,int d);
int isleapyear( );
void print( );
private:
int year,month,day;
};
//以上存入 date.hpp文件中
//日期类的实现
#include”date.hpp”
void DATE::setdate(int y,int m,int d)
{
year=y;
month=m;
day=d;
}
int DATE::isleapyear( )
{
return (year%4==0&&year%100!=0)||(year%400==0);
}
void DATE::print( )
{
cout<<year<<?,?<<month<<?,?<<day<<endl;
}
//以上存入 date.cpp文件中
//分析下列程序的输出结果
#include<iostream.h>
#include”date.cpp”
void main( )
{
DATE d1,d2;
d1.setdate(1996,5,4);
d2.setdate(2004,11,8);
int leap=d1.isleapyear( );
cout<<leap<<endl;
d1.print( );
d2.print( );
}
程序执行结果:
1
1996,5,4
2004,11,8
§ 5.4 对象的初始化
对象的初始化问题
构造函数
构造函数的含义及特点
默认构造函数
析构函数
§ 5.4 对象的初始化
类对象成员不能在类定义时初始化
class FILE
{
public:
……
private:
int length=0;
char file_name[]=“test.cpp”;
};
类是一种模板,类定义时并不分配内存
§ 5.4 对象的初始化
另一种情况:
FILE myfile;
myfile.file_name[]=“test.cpp”;
数据成员 file_name是私有的,外界不能访问,
只能通过类中定义的成员函数来访问
§ 5.4 对象的初始化
类成员的初始化在对象创建时进行
FILE my_file(“test.cpp”);
FILE your_file(“noname.cpp”);
实现,构造函数
§ 5.4 对象的初始化
构造函数与析构函数
在 C++中,类的成员函数有两种:
⑴ 构造函数与析构函数
⑵ 普通的成员函数
构造函数与析构函数是在类定义体中说明的两个特殊的成员函数
§ 5.4 对象的初始化
class FILE
{
public:
FILE(char name[]); //构造函数
……
private:
int length;
char file_name[100];
};
§ 5.4 对象的初始化
构造函数的实现
FILE::FILE(char name[])
{
strcpy(file_name,name);
length=0;
}
§ 5.4 对象的初始化构造函数的调用时间
在对象建立时由系统自动调用
FILE my_file(“myfile.cpp”);
FILE *pfile=new FILE(“yourfile.cpp”);
§ 5.4 对象的初始化构造函数的含义
直接含义:
对象建立时,初始化对象的数据成员
间接含义:
构造一个具有意义的、正确的对象
§ 5.4 对象的初始化构造函数的特点
是类的成员函数
函数名与类名相同 (区别于其他成员函数)
无返回值,void也不能有 (只进行初始化)
由系统自动调用 (当对象被创建时)
必须是公有 (public)成员 (系统调用也遵守访问控制规则)
可以重载
可以初始化常量数据成员
§ 5.4 对象的初始化
构造函数举例
#include<iostream.h>
class RECT
{
public:
RECT(int l,int w);
void area( );
private:
int length,width;
};
RECT::RECT(int l,int w)
{
length=l;
width=w;
}
void RECT::area( )
{
cout<<“area=”<<length*width<<endl;
}
void main( )
{
RECT a(5,8);
RECT b(9,4);
a.area( );
b.area( );
}
§ 5.4 对象的初始化
程序执行结果,内存分配情况:
area=40
area=36
5
8
9
4
……
a
b
§ 5.4 对象的初始化
构造函数的重载
class POLYGON
{
public:
POLYGON( ); //默认构造函数
POLYGON(int edgNum);
POLYGON(char color,int edgNum=3);
private:
int edg_num; //边的个数
char line_color; //线的颜色
POINT point[100]; //各顶点位置
};
§ 5.4 对象的初始化
默认构造函数
POLYGON:,POLYGON( )
{
edg_num=3;
line_ color=0;
for(int i=0; i<edg_num; i++)
point[i].x=point[i].y=0;
}
每个顶点坐标 x,y位置初始值为 0
§ 5.4 对象的初始化
带参数的构造函数
POLYGON:,POLYGON(int edgNum)
{
edg_num= edgNum;
line_ color=0;
for(int i=0; i<edg_num; i++)
point[i].x=point[i].y=0;
}
给定边数的多边形
§ 5.4 对象的初始化
创建两个对象,(在主程序中)
POLYGON pg1; //自动调用默认构造函数
POLYGON pg2(4); //自动调用带参数构造函数
§ 5.4 对象的初始化默认构造函数
每个类必须有一个构造函数
如果没定义构造函数,则 C++系统提供默认构造函数
(do nothing)
如果定义了构造函数,则 C++不再提供默认构造函数
如,POLYGON pg1; //无初始化值的对象必须要有一个默认的构造函数!
§ 5.4 对象的初始化析构函数
类的特殊的成员函数
构造函数在类的对象创建时由系统自动调用的函数
析构函数在类的对象生命期结束时由系统自动调用的函数
§ 5.4 对象的初始化
例:
#include<iostream.h>
class DEMO_CLASS
{
public:
DEMO_CLASS(int i);
~ DEMO_CLASS( ); //析构函数
};
DEMO_CLASS:,DEMO_CLASS(int i)
{
cout<<“Initial value is:”<<i<<endl;
return;
}
DEMO_CLASS:,~DEMO_CLASS( )
{
cout<<“Goodby!\n”;
return;
}
void main( )
{
DEMO_CLASS obj(30); //创建一个对象 obj
cout<<“This is the end of main( ).\n”;
}
§ 5.4 对象的初始化
程序执行结果:
Initial value is,30
This is the end of main( ).
Goodby!
说明:
⑴ 对象 obj的作用域及生命期在 main( )内,对象创建时自动调用构造函数
⑵ 程序执行到 main( )的 ‘ }?处,则 obj的生命期结束,
系统自动调用析构函数,并收回 obj所占用的内存空间
§ 5.4 对象的初始化析 构函数的特点:
是类的成员函数
函数名为类名前加 ‘ ~? (逻辑反运算符)
无返回值,void也不能有
无参数
由系统自动调用 (当对象生命期结束时)
必须是公有 (public)成员 (系统调用也遵守访问控制规则)
不可以重载
§ 5.4 对象的初始化析 构函数的意义
直接意义
-在对象退出生命期前由系统调用的函数
间接意义
-对象能正确、合理地退出生命期
如,释放占用资源,通知有关对象
§ 5.4 对象的初始化构造函数和析构函数
相同点
-由系统自动调用
-没有返回值
-公有函数
不同点
-作用不同,调用时间不同
-构造函数可以重载,可以有参数
§ 5.5 特殊的类成员
常量成员对象一旦定义,就不能更改该成员
静态成员不属于对象的成员,由类的所有对象共享
对象成员一个类的对象作为另一个类的数据成员
§ 5.5 特殊的类成员
常量成员
静态成员
对象成员
§ 5.5 特殊的类成员
常量成员,对象一旦定义,就不能更改该成员
用 const 说明的成员,称为常量成员
常量成员的定义常量数据成员的定义,const int edg_num;
常量成员函数的定义,void print( ) const;
§ 5.5 特殊的类成员
常量数据成员只能初始化,不能被赋值
例,
class SILLY
{
public:
SILLY( )
{
ten=10;
……
}
……
private:
const int ten;
};
常量不能赋值!
§ 5.5 特殊的类成员
常量成员的初始化
class SILLY
{
public:
SILLY( ):ten(10) //初始化常量数据成员
{
…… //此处不能再对 ten赋值
}
private:
const int ten;
……
};
成员初始化参数表
§ 5.5 特殊的类成员
例:定义一个多边形的类
class POLYGON
{
public:
……
private:
const int edg_num; //边数确定的对象
char line_color;
};
注,常量成员不能作为左值
§ 5.5 特殊的类成员
方法一
POLYGON:,POLYGON( ):edg_num(3)
{
line_ color=0;
for( ……)
……;
}
方法二
POLYGON:,POLYGON(int edgNum):edg_num(edgNum)
{
line_ color=0;
for( ……)
……;
}
用构造函数的参数 edgNum的值初始化常量成员 edg_num
§ 5.5 特殊的类成员
常量成员的使用注意:
⑴ 常量成员只能初始化,不能赋值
⑵ 常量成员的初始化 必须在成员初始化参数表中进行
§ 5.5 特殊的类成员
非常量成员,也可在成员初始化参数表中进行初始化
例:
POLYGON:,POLYGON(char color,int edgNum)
:edg_num(edgNum),line_color(color)
{
for(int i=0;…….)
……
}
§ 5.5 特殊的类成员
静态成员
-用 static说明的成员
静态成员分为:
-静态数据成员
-静态函数成员
静态成员由类的所有对象共享
(不属于某个对象)
§ 5.5 特殊的类成员
例,要记录 FILE类中有多少个文件对象
class FILE
{
public:
static int get_file_number( ) //静态成员函数
{
return file_number;
}
private:
static int file_number; //静态数据成员
};
§ 5.5 特殊的类成员
理解静态成员
FILE类
file_number(4)
get_ File_number( )
file1
“Afile”
file2
“Bfile”
file4
“Dfile”
file3
“Cfile”
对象共享
§ 5.5 特殊的类成员静态成员的使用
⑴ 静态成员的初始化必须在全局范围进行
int FILE::file_number=0;
class FILE
{
private:
static int file_number=0;
};
void main( )
{
int FILE::file_number=0;
}
§ 5.5 特殊的类成员
⑵ 静态数据成员可以由所有成员函数访问
void FILE::incFileNum( )
{
file_number++;
}
⑶ 静态成员函数只可以访问静态数据成员
static int get_file_number( ) //静态成员函数
{
cout<<file_name;
……
return file_number;
}
//是对象的特征
§ 5.5 特殊的类成员静态成员的用途
记录对象个数 (用来保存流动变化的对象个数 )
一般在构造函数中增加,析构函数中递减
所有对象共享资源 (它不属于某个对象)
如:内存,文件,数据库,打印机等
例:分析程序执行结果
#include<iostream.h>
class MYCLASS
{
public:
MYCLASS(int a,int b,int c);
void get_number( );
void get_sum( );
private:
int x,y,z;
static int sum;
};
int MYCLASS::sum=0;
MYCLASS:,MYCLASS(int a,int b,int c);
{
x=a; y=b; z=c;
sum+=x+y+z;
}
void MYCLASS:,get_number( )
{
cout<<“Number=“<<x<<?,?<<y<<?,?<<z<<endl;
}
void MYCLASS:,get_sum( )
{
cout<<“Sum=“<<sum<<endl;
}
void main( )
{
MYCLASS m(3,7,10),n(14,19,11);
m.get_number( );
n,get_number( );
m.get_sum( );
n,get_sum( );
}
§ 5.5 特殊的类成员执行结果:
Number=3,7,10
Number=14,19,11
Sum=64
Sum=64
Sum的值无论对 m或 n
两个对象都是相等的
§ 5.5 特殊的类成员
对象成员一个或多个对象作为另一个类的数据成员。
这种具有类类型的数据成员称为“对象成员”
§ 5.5 特殊的类成员
例:
class C
{
public:
……
private:
CLASS_1 obj1;
CLASS_2 obj2;
……
CLASS_N objn;
};
§ 5.5 特殊的类成员
对象成员的初始化
-用成员初始化参数表(同常量成员初始化)
类 C的构造函数
C::C(参数表 0):obj1 (参数表 1),obj2 (参数表 2),…,objn ( 参数表 n)
{
……
} 成员初始化参数表这些参数一般来自,参数表 0”
§ 5.5 特殊的类成员
说明:
①类 C的构造函数必须调用这些对象成员所属类的构造函数
②参数表给出了为调用相应对象成员所属类的构造函数时应提供的参数
③对象成员构造函数的调用顺序取决于这些对象成员在类中声明的次序,与它在初始化参数表中出现的顺序无关
④对象成员析构函数的调用顺序与构造函数刚好相反
例:一个含有对象成员的例子,分析它的输出结果
#include<iostream.h>
class A
{
public:
A(int i,int j)
{
a1=i;
a2=j;
}
void print( )
{
cout<<a1<<?,?<<a2<<endl;
}
private:
int a1,a2;
};
class B
{
public:
B(int i,int j,int k):a(i,j),b(k)
{
}
void print( );
private:
A a;
int b;
};
void B::print( )
{
a.print( );
cout<<b<<endl;
}
void main( )
{
B b(6,7,8);
b.print( );
}
执行结果:
6,7
8
§ 5.5 特殊的类成员
对象成员的意义对象之间的组成关系是对象之间存在的重要关系,它反映了自然界中存在的全局与局部,总体和部分的关系,是面向对象思想的重要组成部分