1
第四章 类与对象
C++语言程序设计
清华大学计算机与信息管理中心
郑 莉
前一页 休息 2
本章主要内容
? 面向对象的思想
? OOP的基本特点
? 类概念和声明
? 对象
? 构造函数
? 析构函数
? 内联成员函数
? 拷贝构造函数
? 类的聚集
前一页 休息 3
回顾:面向过程的设计方法
? 重点,
– 如何实现细节过程,将数据与函数分开。
? 形式:
– 主模块 +若干个子模块( main()+子函数)。
? 特点:
– 自顶向下,逐步求精 ——功能分解。
? 缺点:
– 效率低,程序的可重用性差。
面
向
对
象
的
思
想
前一页 休息 4
面向对象的方法
? 目的:
– 实现软件设计的产业化。
? 观点:
– 自然界是由实体(对象)所组成。
? 程序设计方法:
– 使用面向对象的观点来描述模仿并处理
现实问题。
? 要求:
– 高度概括、分类、和抽象。
面
向
对
象
的
思
想
前一页 休息 5
抽象
抽象是对具体对象(问题)进行概括,
抽出这一类对象的公共性质并加以描述
的过程。
– 先注意问题的本质及描述,其次是实现过
程或细节。
– 数据抽象:描述某类对象的属性或状态
(对象相互区别的物理量)。
– 代码抽象:描述某类对象的共有的行为特
征或具有的功能。
– 抽象的实现:通过类的声明。
OOP
的
基
本
特
点
前一页 休息 6
抽象实例 —— 钟表
? 数据抽象:
int Hour,int Minute,int Second
? 代码抽象:
SetTime(),ShowTime()
OOP
的
基
本
特
点
前一页 休息 7
抽象实例 —— 钟表类
class Clock
{
public,
void SetTime(int NewH,int NewM,
int NewS);
void ShowTime();
private,
int Hour,Minute,Second;
};
OOP
的
基
本
特
点
前一页 休息 8
抽象实例 —— 人
?数据抽象:
char *name,char *sex,int age,int id
?代码抽象:
生物属性角度:
GetCloth(),Eat(),Step(),…
社会属性角度:
Work(),Promote(),…
OOP
的
基
本
特
点
前一页 休息 9
封装
将抽象出的数据成员、代码成员相结
合,将它们视为一个整体。
– 目的是曾强安全性和简化编程,使用者
不必了解具体的实现细节,而只需要通
过外部接口,以特定的访问权限,来使
用类的成员。
– 实现封装:类声明中的 {}
OOP
的
基
本
特
点
前一页 休息 10
封装
? 实例:
class Clock
{
public,void SetTime(int NewH,int NewM,
int NewS);
void ShowTime();
private,int Hour,Minute,Second;
};
边界
特定的访问权限
OOP
的
基
本
特
点
外部接口
前一页 休息 11
继承与派生
是 C++中支持层次分类的一种机制,
允许程序员在保持原有类特性的基础上,
进行更具体的说明。
实现:声明派生类 —— 第七章
OOP
的
基
本
特
点
前一页 休息 12
多态性
? 多态:同一名称,不同的功能实现方式。
? 目的:达到行为标识统一,减少程序中标
识符的个数。
? 实现:重载函数和虚函数 —— 第八章
OOP
的
基
本
特
点
前一页 休息 13
c++中的类
? 类是具有相同属性和行为的一组对象
的集合,它为属于该类的全部对象提
供了统一的抽象描述,其内部包括属
性和行为两个主要部分。
? 利用类可以实现数据的封装、隐藏、
继承与派生。
? 利用类易于编写大型复杂程序,其模
块化程度比 C中采用函数更高。
类
和
对
象
前一页 休息 14
类的声明形式
类是一种用户自定义类型,声明形式:
class 类名称
{
public:
公有成员 (外部接口)
private:
私有成员
protected:
保护型成员
}
类
和
对
象
前一页 休息 15
公有类型成员
在关键字 public后面声明,它们是类
与外部的接口,任何外部函数都可以访
问公有类型数据和函数。
前一页 休息 16
私有类型成员
在关键字 private后面声明,只允许
本类中的函数访问,而类外部的任何函
数都不能访问。
如果 紧跟在类名称的后面声明私有成员,
则 关键字 private可以 省略。
前一页 休息 17
保护类型
与 private类似,其差别表现在继承与
派生时对派生类的影响不同,第七章讲。
前一页 休息 18
类的成员
class Clock
{
public,
void SetTime(int NewH,int NewM,
int NewS);
void ShowTime();
private,
int Hour,Minute,Second;
};
类
和
对
象
成员数据
成员函数
void Clock,,SetTime(int NewH,int NewM,
int NewS)
{
Hour=NewH;
Minute=NewM;
Second=NewS;
}
void Clock,,ShowTime()
{
cout<<Hour<<":"<<Minute<<":"<<Second;
}
前一页 休息 20
成员数据
? 与一般的变量声明相同,但需要将它
放在类的声明体中。
前一页 休息 21
成员函数
? 在类中说明原形,可以在类外给出函数
体实现,并在函数名前使用类名加以限
定。也可以直接在类中给出函数体,形
成内联成员函数。
? 允许声明重载函数和带缺省形参值的函
数
前一页 休息 22
内联成员函数
? 为了提高运行时的效率,对于较简单
的函数可以声明为内联形式。
? 内联函数体中不要有复杂结构(如循
环语句和 switch语句)。
? 在类中声明内联成员函数的方式:
– 将函数体放在类的声明中。
– 使用 inline关键字。
类
和
对
象
前一页 休息 23
内联成员函数举例 (一 )
class Point
{
public:
void Init(int initX,int initY)
{
X=initX;
Y=initY;
}
int GetX() {return X;}
int GetY() {return Y;}
private:
int X,Y;
};
类
和
对
象
前一页 休息 24
内联成员函数举例 (二 )
class Point
{
public:
void Init(int initX,int initY);
int GetX();
int GetY();
private:
int X,Y;
};
类
和
对
象
inline void Point::Init(int initX,int initY)
{
X=initX;
Y=initY;
}
inline int Point::GetX()
{
return X;
}
inline int Point::GetY()
{
return Y;
}
前一页 休息 26
对象
? 类的对象是该类的某一特定实体,即
类类型的变量。
? 声明形式:
类名 对象名;
? 例:
Clock myClock;
类
和
对
象
前一页 休息 27
类中成员的访问方式
? 类中成员互访
– 直接使用成员名
? 类外访问
– 使用,对象名,成员名,方式访问 public
属性的成员
类
和
对
象
前一页 休息 28
例 4-1类的应用举例
#include<iostream.h>
class Clock
{
......//类的声明略
}
//......类的实现略
void main(void)
{ Clock myClock;
myClock.SetTime(8,30,30);
myClock.ShowTime();
}
类
和
对
象
前一页 休息 29
构造函数
? 构造函数的作用是在对象被创建时使
用特定的值构造对象,或者说将对象
初始化 为一个特定的状态。
? 在对象创建时 由系统自动调用 。
? 如果程序中未声明,则系统自动产生
出一个 缺省形式 的构造函数
? 允许为 内联 函数,重载 函数,带缺省
形参值 的函数
构
造
函
数
和
析
构
函
数
前一页 休息 30
构造函数举例
class Clock
{
public:
Clock (int NewH,int NewM,int NewS);//构造函数
void SetTime(int NewH,int NewM,int NewS);
void ShowTime();
private:
int Hour,Minute,Second;
};
构
造
函
数
和
析
构
函
数
构造函数的实现:
Clock::Clock(int NewH,int NewM,int NewS)
{
Hour= NewH;
Minute= NewM;
Second= NewS;
}
建立对象时构造函数的作用:
void main()
{
Clock c (0,0,0); //隐含调用构造函数,将初始值作为实参。
c.ShowTime();
}
前一页 休息 32
拷贝构造函数
拷贝构造函数是一种特殊的构造函数,其形参为
本类的对象引用。
class 类名
{ public,
类名(形参); //构造函数
类名(类名 &对象名); //拷贝构造函数
...
};
类名,,类名(类名 &对象名) //拷贝构造函数的实现
{ 函数体 }
构
造
函
数
和
析
构
函
数
前一页 休息 33
例 4-2 拷贝构造函数举例
class Point
{
public:
Point(int xx=0,int yy=0){X=xx; Y=yy;}
Point(Point& p);
int GetX() {return X;}
int GetY() {return Y;}
private:
int X,Y;
};
构
造
函
数
和
析
构
函
数
Point::Point (Point& p)
{
X=p.X;
Y=p.Y;
cout<<"拷贝构造函数被调用 "<<endl;
}
前一页 休息 35
例 4-2 拷贝构造函数举例
? 当用类的一个对象去初始化该类的另一
个对象时系统自动调用它实现拷贝赋值。
void main(void)
{ Point A(1,2);
Point B(A); //拷贝构造函数被调用
cout<<B.GetX()<<endl;
}
构
造
函
数
和
析
构
函
数
前一页 休息 36
例 4-2拷贝构造函数举例
? 若函数的形参为类对象,调用函数时,
实参赋值给形参,系统自动调用拷贝
构造函数。例如:
void fun1(Point p)
{ cout<<p.GetX()<<endl;
}
void main()
{ Point A(1,2);
fun1(A); //调用拷贝构造函数
}
构
造
函
数
和
析
构
函
数
前一页 休息 37
拷贝构造函数 (例 4-2)
? 当函数的返回值是类对象时,系统自动调用
拷贝构造函数。例如:
Point fun2()
{ Point A(1,2);
return A; //调用拷贝构造函数
}
void main()
{
Point B;
B=fun2();
}
构
造
函
数
和
析
构
函
数
前一页 休息 38
拷贝构造函数
如果程序员没有为类声明拷贝初始
化构造函数,则编译器自己生成一个拷
贝构造函数。
这个构造函数执行的功能是:用作
为初始值的对象的每个数据成员的值,
初始化将要建立的对象的对应数据成员。
构
造
函
数
和
析
构
函
数
前一页 休息 39
析构函数
? 完成对象被删除前的一些清理工作。
? 在对象的生存期结束的时刻系统自动
调用它,然后再释放此对象所属的空
间。
? 如果程序中未声明析构函数,编译器
将自动产生一个缺省的析构函数。
构
造
函
数
和
析
构
函
数
前一页 休息 40
构造函数和析构函数举例
#include<iostream.h>
class Point
{
public:
Point(int xx,int yy);
~Point();
//...其它函数原形
private:
int X,int Y;
};
构
造
函
数
和
析
构
函
数
Point::Point(int xx,int yy)
{ X=xx; Y=yy;
}
Point::~Point()
{
}
//...其它函数的实现略
前一页 休息 42
类的应用举例 (例 4-3)
一圆型游泳池如图所示,现在需在其周围
建一圆型过道,并在其四周围上栅栏。栅栏价格
为 35元 /米,过道造价为 20元 /平方米。过道宽度
为 3米,游泳池半径由键盘输入。要求编程计算
并输出过道和栅栏的造价。
游泳池
过道
#include <iostream.h>
const float PI = 3.14159;
const float FencePrice = 35;
const float ConcretePrice = 20;
//声明类 Circle 及其数据和方法
class Circle
{
private:
float radius;
public:
Circle(float r); //构造函数
float Circumference() const; //圆周长
float Area() const; //圆面积
};
// 类的实现
// 构造函数初始化数据成员 radius
Circle::Circle(float r)
{radius=r}
// 计算圆的周长
float Circle::Circumference() const
{
return 2 * PI * radius;
}
// 计算圆的面积
float Circle::Area() const
{
return PI * radius * radius;
}
void main ()
{
float radius;
float FenceCost,ConcreteCost;
// 提示用户输入半径
cout<<"Enter the radius of the pool,";
cin>>radius;
// 声明 Circle 对象
Circle Pool(radius);
Circle PoolRim(radius + 3);
// 计算栅栏造价并输出
FenceCost = PoolRim.Circumference() * FencePrice;
cout << "Fencing Cost is ¥ " << FenceCost << endl;
// 计算过道造价并输出
ConcreteCost = (PoolRim.Area() -
Pool.Area())*ConcretePrice;
cout << "Concrete Cost is ¥ " << ConcreteCost << endl;
}
运行结果
Enter the radius of the pool,10
Fencing Cost is ¥ 2858.85
Concrete Cost is ¥ 4335.39
前一页 休息 47
什么是类的组合
? 类中的成员数据是另一个类的对象。
? 可以在已有的抽象的基础上实现更复
杂的抽象。
类
的
组
合
前一页 休息 48
举例
class Point
{ private:
float x,y; //点的坐标
public:
Point(float h,float v); //构造函数
float GetX(void); //取 X坐标
float GetY(void); //取 Y坐标
void Draw(void); //在 (x,y)处画点
};
//...函数的实现略
类
的
组
合
class Line
{
private:
Point p1,p2; //线段的两个端点
public:
Line(Point a,Point b); //构造函数
Void Draw(void); //画出线段
};
//...函数的实现略
前一页 休息 50
类组合的构造函数设计
? 原则:不仅要负责对本类中的基本类型成
员数据赋初值,也要对对象成员初始化。
? 声明形式:
类名,:类名 (对象成员所需的形参,本类成员形参 )
:对象 1(参数 ),对象 2(参数 ),......
{ 本类初始化 }
类
的
组
合
前一页 休息 51
类组合的构造函数调用
? 构造函数调用顺序:先调用内嵌对象
的构造函数(按内嵌时的声明顺序,
先声明者先构造)。然后调用本类的
构造函数。(析构函数的调用顺序相
反)
? 若调用缺省构造函数(即无形参的),
则内嵌对象的初始化也将调用相应的
缺省构造函数。
类
的
组
合
前一页 休息 52
类的组合举例(二)
class Part //部件类
{
public:
Part();
Part(int i);
~Part();
void Print();
private:
int val;
};
类
的
组
合
class Whole
{
public:
Whole();
Whole(int i,int j,int k);
~Whole();
void Print();
private:
Part one;
Part two;
int date;
};
Whole::Whole()
{
date=0;
}
Whole::Whole(int i,int j,int k):
two(i),one(j),date(k)
{}
//...其它函数的实现略
前一页 休息 55
前向引用声明
? 类应该先声明,后使用
? 如果需要在某个类的声明之前,引用
该类,则应进行前向引用声明。
? 前向引用声明只为程序引入一个标识
符,但具体声明在其它地方。
前一页 休息 56
前向引用声明举例
class A
{ public:
void f(B b);
};
class B
{ public:
void g(A a);
};
class B; //前向引用声明
前一页 休息 57
前向引用声明注意事项
? 使用前行引用声明虽然可以解决一些问题,但它
并不是万能的。需要注意的是,尽管使用了前向
引用声明,但是在提供一个完整的类声明之前,
不能声明该类的对象,也不能在内联成员函数中
使用该类的对象。请看下面的程序段:
class Fred; //前向引用声明
class Barney {
Fred x; //错误:类 Fred的声明尚不完善
};
class Fred {
Barney y;
};
前一页 休息 59
前向引用声明注意事项
? 应该记住:当你使用前向引用声明时,
你只能使用被声明的符号,而不能涉
及类的任何细节。
前一页 休息 60
声明及用途
template <模板参数表 >
类声明
使用类模板使用户可以为类声明一
种模式,使得类中的某些数据成员、
某些成员函数的参数、某些成员函数
的返回值,能取任意类型(包括系统
预定义的和用户自定义的)。
例:例 4.5
类
模
板
前一页 休息 61
例 4-5 类模板应用举例
#include <iostream.h>
#include <stdlib.h>
// 结构体 Student
struct Student
{
int id; //学号
float gpa; //平均分
};
类
模
板
template <class T>
//类模板:实现对任意类型数据进行存取
class Store
{ private:
T item; // 用于存放任意类型的数据
int haveValue; // 用于标记 item是否已被存入内容
public:
Store(void); // 缺省形式(无形参)的构造函数
T GetElem(void); //提取数据函数
void PutElem(T x); //存入数据函数
};
// 缺省形式构造函数的实现
template <class T>
Store<T>::Store(void),haveValue(0) {}
template <class T> // 提取数据函数的实现
T Store<T>::GetElem(void)
{ // 如果试图提取未初始化的数据,则终止程序
if (haveValue == 0)
{ cout << "No item present!" << endl;
exit(1);
}
return item; // 返回 item中存放的数据
}
template <class T> // 存入数据函数的实现
void Store<T>::PutElem(T x)
{ haveValue++; // 将 haveValue 置为 TRUE,表示
item中已存入数值
item = x; // 将 x值存入 item
}
void main(void)
{ Student g= {1000,23};
Store<int> S1,S2;
Store<Student> S3;
Store<double> D;
S1.PutElem(3);
S2.PutElem(-7);
cout << S1.GetElem() << " " << S2.GetElem() << endl;
S3.PutElem(g);
cout << "The student id is " << S3.GetElem().id << endl;
cout << "Retrieving object D " ;
cout << D.GetElem() << endl; //输出对象 D的数据成员
// 由于 D未经初始化,在执行函数 D.GetElement()时出错
}
前一页 休息 65
作业
? 复习第四章,预习第五章
? 4-10,4-11,4-12
? 实验四
第四章 类与对象
C++语言程序设计
清华大学计算机与信息管理中心
郑 莉
前一页 休息 2
本章主要内容
? 面向对象的思想
? OOP的基本特点
? 类概念和声明
? 对象
? 构造函数
? 析构函数
? 内联成员函数
? 拷贝构造函数
? 类的聚集
前一页 休息 3
回顾:面向过程的设计方法
? 重点,
– 如何实现细节过程,将数据与函数分开。
? 形式:
– 主模块 +若干个子模块( main()+子函数)。
? 特点:
– 自顶向下,逐步求精 ——功能分解。
? 缺点:
– 效率低,程序的可重用性差。
面
向
对
象
的
思
想
前一页 休息 4
面向对象的方法
? 目的:
– 实现软件设计的产业化。
? 观点:
– 自然界是由实体(对象)所组成。
? 程序设计方法:
– 使用面向对象的观点来描述模仿并处理
现实问题。
? 要求:
– 高度概括、分类、和抽象。
面
向
对
象
的
思
想
前一页 休息 5
抽象
抽象是对具体对象(问题)进行概括,
抽出这一类对象的公共性质并加以描述
的过程。
– 先注意问题的本质及描述,其次是实现过
程或细节。
– 数据抽象:描述某类对象的属性或状态
(对象相互区别的物理量)。
– 代码抽象:描述某类对象的共有的行为特
征或具有的功能。
– 抽象的实现:通过类的声明。
OOP
的
基
本
特
点
前一页 休息 6
抽象实例 —— 钟表
? 数据抽象:
int Hour,int Minute,int Second
? 代码抽象:
SetTime(),ShowTime()
OOP
的
基
本
特
点
前一页 休息 7
抽象实例 —— 钟表类
class Clock
{
public,
void SetTime(int NewH,int NewM,
int NewS);
void ShowTime();
private,
int Hour,Minute,Second;
};
OOP
的
基
本
特
点
前一页 休息 8
抽象实例 —— 人
?数据抽象:
char *name,char *sex,int age,int id
?代码抽象:
生物属性角度:
GetCloth(),Eat(),Step(),…
社会属性角度:
Work(),Promote(),…
OOP
的
基
本
特
点
前一页 休息 9
封装
将抽象出的数据成员、代码成员相结
合,将它们视为一个整体。
– 目的是曾强安全性和简化编程,使用者
不必了解具体的实现细节,而只需要通
过外部接口,以特定的访问权限,来使
用类的成员。
– 实现封装:类声明中的 {}
OOP
的
基
本
特
点
前一页 休息 10
封装
? 实例:
class Clock
{
public,void SetTime(int NewH,int NewM,
int NewS);
void ShowTime();
private,int Hour,Minute,Second;
};
边界
特定的访问权限
OOP
的
基
本
特
点
外部接口
前一页 休息 11
继承与派生
是 C++中支持层次分类的一种机制,
允许程序员在保持原有类特性的基础上,
进行更具体的说明。
实现:声明派生类 —— 第七章
OOP
的
基
本
特
点
前一页 休息 12
多态性
? 多态:同一名称,不同的功能实现方式。
? 目的:达到行为标识统一,减少程序中标
识符的个数。
? 实现:重载函数和虚函数 —— 第八章
OOP
的
基
本
特
点
前一页 休息 13
c++中的类
? 类是具有相同属性和行为的一组对象
的集合,它为属于该类的全部对象提
供了统一的抽象描述,其内部包括属
性和行为两个主要部分。
? 利用类可以实现数据的封装、隐藏、
继承与派生。
? 利用类易于编写大型复杂程序,其模
块化程度比 C中采用函数更高。
类
和
对
象
前一页 休息 14
类的声明形式
类是一种用户自定义类型,声明形式:
class 类名称
{
public:
公有成员 (外部接口)
private:
私有成员
protected:
保护型成员
}
类
和
对
象
前一页 休息 15
公有类型成员
在关键字 public后面声明,它们是类
与外部的接口,任何外部函数都可以访
问公有类型数据和函数。
前一页 休息 16
私有类型成员
在关键字 private后面声明,只允许
本类中的函数访问,而类外部的任何函
数都不能访问。
如果 紧跟在类名称的后面声明私有成员,
则 关键字 private可以 省略。
前一页 休息 17
保护类型
与 private类似,其差别表现在继承与
派生时对派生类的影响不同,第七章讲。
前一页 休息 18
类的成员
class Clock
{
public,
void SetTime(int NewH,int NewM,
int NewS);
void ShowTime();
private,
int Hour,Minute,Second;
};
类
和
对
象
成员数据
成员函数
void Clock,,SetTime(int NewH,int NewM,
int NewS)
{
Hour=NewH;
Minute=NewM;
Second=NewS;
}
void Clock,,ShowTime()
{
cout<<Hour<<":"<<Minute<<":"<<Second;
}
前一页 休息 20
成员数据
? 与一般的变量声明相同,但需要将它
放在类的声明体中。
前一页 休息 21
成员函数
? 在类中说明原形,可以在类外给出函数
体实现,并在函数名前使用类名加以限
定。也可以直接在类中给出函数体,形
成内联成员函数。
? 允许声明重载函数和带缺省形参值的函
数
前一页 休息 22
内联成员函数
? 为了提高运行时的效率,对于较简单
的函数可以声明为内联形式。
? 内联函数体中不要有复杂结构(如循
环语句和 switch语句)。
? 在类中声明内联成员函数的方式:
– 将函数体放在类的声明中。
– 使用 inline关键字。
类
和
对
象
前一页 休息 23
内联成员函数举例 (一 )
class Point
{
public:
void Init(int initX,int initY)
{
X=initX;
Y=initY;
}
int GetX() {return X;}
int GetY() {return Y;}
private:
int X,Y;
};
类
和
对
象
前一页 休息 24
内联成员函数举例 (二 )
class Point
{
public:
void Init(int initX,int initY);
int GetX();
int GetY();
private:
int X,Y;
};
类
和
对
象
inline void Point::Init(int initX,int initY)
{
X=initX;
Y=initY;
}
inline int Point::GetX()
{
return X;
}
inline int Point::GetY()
{
return Y;
}
前一页 休息 26
对象
? 类的对象是该类的某一特定实体,即
类类型的变量。
? 声明形式:
类名 对象名;
? 例:
Clock myClock;
类
和
对
象
前一页 休息 27
类中成员的访问方式
? 类中成员互访
– 直接使用成员名
? 类外访问
– 使用,对象名,成员名,方式访问 public
属性的成员
类
和
对
象
前一页 休息 28
例 4-1类的应用举例
#include<iostream.h>
class Clock
{
......//类的声明略
}
//......类的实现略
void main(void)
{ Clock myClock;
myClock.SetTime(8,30,30);
myClock.ShowTime();
}
类
和
对
象
前一页 休息 29
构造函数
? 构造函数的作用是在对象被创建时使
用特定的值构造对象,或者说将对象
初始化 为一个特定的状态。
? 在对象创建时 由系统自动调用 。
? 如果程序中未声明,则系统自动产生
出一个 缺省形式 的构造函数
? 允许为 内联 函数,重载 函数,带缺省
形参值 的函数
构
造
函
数
和
析
构
函
数
前一页 休息 30
构造函数举例
class Clock
{
public:
Clock (int NewH,int NewM,int NewS);//构造函数
void SetTime(int NewH,int NewM,int NewS);
void ShowTime();
private:
int Hour,Minute,Second;
};
构
造
函
数
和
析
构
函
数
构造函数的实现:
Clock::Clock(int NewH,int NewM,int NewS)
{
Hour= NewH;
Minute= NewM;
Second= NewS;
}
建立对象时构造函数的作用:
void main()
{
Clock c (0,0,0); //隐含调用构造函数,将初始值作为实参。
c.ShowTime();
}
前一页 休息 32
拷贝构造函数
拷贝构造函数是一种特殊的构造函数,其形参为
本类的对象引用。
class 类名
{ public,
类名(形参); //构造函数
类名(类名 &对象名); //拷贝构造函数
...
};
类名,,类名(类名 &对象名) //拷贝构造函数的实现
{ 函数体 }
构
造
函
数
和
析
构
函
数
前一页 休息 33
例 4-2 拷贝构造函数举例
class Point
{
public:
Point(int xx=0,int yy=0){X=xx; Y=yy;}
Point(Point& p);
int GetX() {return X;}
int GetY() {return Y;}
private:
int X,Y;
};
构
造
函
数
和
析
构
函
数
Point::Point (Point& p)
{
X=p.X;
Y=p.Y;
cout<<"拷贝构造函数被调用 "<<endl;
}
前一页 休息 35
例 4-2 拷贝构造函数举例
? 当用类的一个对象去初始化该类的另一
个对象时系统自动调用它实现拷贝赋值。
void main(void)
{ Point A(1,2);
Point B(A); //拷贝构造函数被调用
cout<<B.GetX()<<endl;
}
构
造
函
数
和
析
构
函
数
前一页 休息 36
例 4-2拷贝构造函数举例
? 若函数的形参为类对象,调用函数时,
实参赋值给形参,系统自动调用拷贝
构造函数。例如:
void fun1(Point p)
{ cout<<p.GetX()<<endl;
}
void main()
{ Point A(1,2);
fun1(A); //调用拷贝构造函数
}
构
造
函
数
和
析
构
函
数
前一页 休息 37
拷贝构造函数 (例 4-2)
? 当函数的返回值是类对象时,系统自动调用
拷贝构造函数。例如:
Point fun2()
{ Point A(1,2);
return A; //调用拷贝构造函数
}
void main()
{
Point B;
B=fun2();
}
构
造
函
数
和
析
构
函
数
前一页 休息 38
拷贝构造函数
如果程序员没有为类声明拷贝初始
化构造函数,则编译器自己生成一个拷
贝构造函数。
这个构造函数执行的功能是:用作
为初始值的对象的每个数据成员的值,
初始化将要建立的对象的对应数据成员。
构
造
函
数
和
析
构
函
数
前一页 休息 39
析构函数
? 完成对象被删除前的一些清理工作。
? 在对象的生存期结束的时刻系统自动
调用它,然后再释放此对象所属的空
间。
? 如果程序中未声明析构函数,编译器
将自动产生一个缺省的析构函数。
构
造
函
数
和
析
构
函
数
前一页 休息 40
构造函数和析构函数举例
#include<iostream.h>
class Point
{
public:
Point(int xx,int yy);
~Point();
//...其它函数原形
private:
int X,int Y;
};
构
造
函
数
和
析
构
函
数
Point::Point(int xx,int yy)
{ X=xx; Y=yy;
}
Point::~Point()
{
}
//...其它函数的实现略
前一页 休息 42
类的应用举例 (例 4-3)
一圆型游泳池如图所示,现在需在其周围
建一圆型过道,并在其四周围上栅栏。栅栏价格
为 35元 /米,过道造价为 20元 /平方米。过道宽度
为 3米,游泳池半径由键盘输入。要求编程计算
并输出过道和栅栏的造价。
游泳池
过道
#include <iostream.h>
const float PI = 3.14159;
const float FencePrice = 35;
const float ConcretePrice = 20;
//声明类 Circle 及其数据和方法
class Circle
{
private:
float radius;
public:
Circle(float r); //构造函数
float Circumference() const; //圆周长
float Area() const; //圆面积
};
// 类的实现
// 构造函数初始化数据成员 radius
Circle::Circle(float r)
{radius=r}
// 计算圆的周长
float Circle::Circumference() const
{
return 2 * PI * radius;
}
// 计算圆的面积
float Circle::Area() const
{
return PI * radius * radius;
}
void main ()
{
float radius;
float FenceCost,ConcreteCost;
// 提示用户输入半径
cout<<"Enter the radius of the pool,";
cin>>radius;
// 声明 Circle 对象
Circle Pool(radius);
Circle PoolRim(radius + 3);
// 计算栅栏造价并输出
FenceCost = PoolRim.Circumference() * FencePrice;
cout << "Fencing Cost is ¥ " << FenceCost << endl;
// 计算过道造价并输出
ConcreteCost = (PoolRim.Area() -
Pool.Area())*ConcretePrice;
cout << "Concrete Cost is ¥ " << ConcreteCost << endl;
}
运行结果
Enter the radius of the pool,10
Fencing Cost is ¥ 2858.85
Concrete Cost is ¥ 4335.39
前一页 休息 47
什么是类的组合
? 类中的成员数据是另一个类的对象。
? 可以在已有的抽象的基础上实现更复
杂的抽象。
类
的
组
合
前一页 休息 48
举例
class Point
{ private:
float x,y; //点的坐标
public:
Point(float h,float v); //构造函数
float GetX(void); //取 X坐标
float GetY(void); //取 Y坐标
void Draw(void); //在 (x,y)处画点
};
//...函数的实现略
类
的
组
合
class Line
{
private:
Point p1,p2; //线段的两个端点
public:
Line(Point a,Point b); //构造函数
Void Draw(void); //画出线段
};
//...函数的实现略
前一页 休息 50
类组合的构造函数设计
? 原则:不仅要负责对本类中的基本类型成
员数据赋初值,也要对对象成员初始化。
? 声明形式:
类名,:类名 (对象成员所需的形参,本类成员形参 )
:对象 1(参数 ),对象 2(参数 ),......
{ 本类初始化 }
类
的
组
合
前一页 休息 51
类组合的构造函数调用
? 构造函数调用顺序:先调用内嵌对象
的构造函数(按内嵌时的声明顺序,
先声明者先构造)。然后调用本类的
构造函数。(析构函数的调用顺序相
反)
? 若调用缺省构造函数(即无形参的),
则内嵌对象的初始化也将调用相应的
缺省构造函数。
类
的
组
合
前一页 休息 52
类的组合举例(二)
class Part //部件类
{
public:
Part();
Part(int i);
~Part();
void Print();
private:
int val;
};
类
的
组
合
class Whole
{
public:
Whole();
Whole(int i,int j,int k);
~Whole();
void Print();
private:
Part one;
Part two;
int date;
};
Whole::Whole()
{
date=0;
}
Whole::Whole(int i,int j,int k):
two(i),one(j),date(k)
{}
//...其它函数的实现略
前一页 休息 55
前向引用声明
? 类应该先声明,后使用
? 如果需要在某个类的声明之前,引用
该类,则应进行前向引用声明。
? 前向引用声明只为程序引入一个标识
符,但具体声明在其它地方。
前一页 休息 56
前向引用声明举例
class A
{ public:
void f(B b);
};
class B
{ public:
void g(A a);
};
class B; //前向引用声明
前一页 休息 57
前向引用声明注意事项
? 使用前行引用声明虽然可以解决一些问题,但它
并不是万能的。需要注意的是,尽管使用了前向
引用声明,但是在提供一个完整的类声明之前,
不能声明该类的对象,也不能在内联成员函数中
使用该类的对象。请看下面的程序段:
class Fred; //前向引用声明
class Barney {
Fred x; //错误:类 Fred的声明尚不完善
};
class Fred {
Barney y;
};
前一页 休息 59
前向引用声明注意事项
? 应该记住:当你使用前向引用声明时,
你只能使用被声明的符号,而不能涉
及类的任何细节。
前一页 休息 60
声明及用途
template <模板参数表 >
类声明
使用类模板使用户可以为类声明一
种模式,使得类中的某些数据成员、
某些成员函数的参数、某些成员函数
的返回值,能取任意类型(包括系统
预定义的和用户自定义的)。
例:例 4.5
类
模
板
前一页 休息 61
例 4-5 类模板应用举例
#include <iostream.h>
#include <stdlib.h>
// 结构体 Student
struct Student
{
int id; //学号
float gpa; //平均分
};
类
模
板
template <class T>
//类模板:实现对任意类型数据进行存取
class Store
{ private:
T item; // 用于存放任意类型的数据
int haveValue; // 用于标记 item是否已被存入内容
public:
Store(void); // 缺省形式(无形参)的构造函数
T GetElem(void); //提取数据函数
void PutElem(T x); //存入数据函数
};
// 缺省形式构造函数的实现
template <class T>
Store<T>::Store(void),haveValue(0) {}
template <class T> // 提取数据函数的实现
T Store<T>::GetElem(void)
{ // 如果试图提取未初始化的数据,则终止程序
if (haveValue == 0)
{ cout << "No item present!" << endl;
exit(1);
}
return item; // 返回 item中存放的数据
}
template <class T> // 存入数据函数的实现
void Store<T>::PutElem(T x)
{ haveValue++; // 将 haveValue 置为 TRUE,表示
item中已存入数值
item = x; // 将 x值存入 item
}
void main(void)
{ Student g= {1000,23};
Store<int> S1,S2;
Store<Student> S3;
Store<double> D;
S1.PutElem(3);
S2.PutElem(-7);
cout << S1.GetElem() << " " << S2.GetElem() << endl;
S3.PutElem(g);
cout << "The student id is " << S3.GetElem().id << endl;
cout << "Retrieving object D " ;
cout << D.GetElem() << endl; //输出对象 D的数据成员
// 由于 D未经初始化,在执行函数 D.GetElement()时出错
}
前一页 休息 65
作业
? 复习第四章,预习第五章
? 4-10,4-11,4-12
? 实验四