第九章类和对象.docx
《第九章类和对象.docx》由会员分享,可在线阅读,更多相关《第九章类和对象.docx(34页珍藏版)》请在冰豆网上搜索。
第九章类和对象
类与对象
传统的结构化语言都是采用面向过程的方法来解决问题。
在面向过程的程序设计方法中,代码和数据是分离的,因此,程序的可维护性较差。
面向对象(ObjectOriented)程序设计方法则是把数据及处理这些数据的函数封装到一个类中,类是C++的一种数据类型,而使用类的变量则称为对象。
函数是将逻辑上有关的语句和数据集合在一起,主要用于执行;而类则是逻辑上有关的函数及其数据的集合,它主要不是用于执行,而是提供所需要的资源。
1.类的定义
在使用一个类之前必须先定义类。
例如在C语言里声明一个结构体类型:
structstudent
{intnum
charname[10];
charsex;
};
structstudentstudent1,student2;
现在我们声明一个类:
classstud//以class开头
{intnum
charname[10];
charsex;//以上3行是数据成员
voiddisplay()//这是成员函数
{cout<<”num:
”<cout<<”name:
”<cout<”sex:
”<}//以上4行是操作代码
};
studstud1,stud2;//定义了两个stud类的对象
定义一个类的语法格式如下:
class类名:
基类名
{
private:
私有成员数据及函数;
protected:
保护成员数据及函数;
public:
公共成员数据及函数;
}[类的对象声明];
一个类含有私有成员(private)、保护成员(protected)和公共成员(public)三部分。
默认时在类中定义的项都是私有的。
私有变量和函数只能被该类本身的成员函数存取或调用;保护成员除可以被本类中的成员函数访问外,还可以被本类派生的类的成员函数访问,因此用于类的继承;公共成员可以被本类以外的函数访问,是类与外部的接口。
类是面向对象程序设计最基本的单元,在设计面向对象程序时,首先要以类的方式描述实际待解决的问题,也就是将问题所要处理的数据定义成类的私有或公共类型的数据,同时将处理问题的方法定义成类的私有或公有的成员函数。
类也可以嵌套声明。
对象
对象是声明为类类型的一个数据项,是类的实际变量,有时也称为类的实例(Instance)。
方法:
类中的成员函数称为“方法”,“方法”是对数据的操作。
方法的具体实现既可以在类的定义内部完成,也可以在类的定义之外进行,而且方法的具体实现既可以和类的定义放在同一个源文件中,也可以放在不同的源文件中。
消息:
外界是通过发“消息”来激活有关方法的。
所谓“消息”,其实就是一个命令,由程序语句来实现。
例如想输出对象stud1中的学生学号、姓名、性别等信息,可以在程序中写
stud1.display();
这就是向对象stud1发出的一个“消息”,通知它执行display“方法”。
例1-1:
Lt1-1.cpp
#include
#include
constdoubleANG_TO_RAD=0.0174532925;
//定义弧度和度之间的转换比例,即弧度的值
classangle//定义类angle
{doublevalue;//类angle的私有数据成员
public:
//类angle的公共数据成员
voidSetValue(double);
doubleGetSine(void);
}deg;//声明类angle的对象deg
voidangle:
:
SetValue(doublea)//类angle的成员函数SetValuede的具体实现
{value=a;
}
doubleangle:
:
GetSine(void)//类angle的成员函数GetSine的具体实现
{doubletemp;
temp=sin(ANG_TO_RAD*value);
returntemp;
}
voidmain()
{deg.SetValue(60.0);//给类angle的成员变量Value赋值
cout<<"Thesineoftheangleis:
";
cout<}
运行结果为:
Thesineoftheangleis:
0.866025
又例:
#include
#include
constdoubleANG_TO_RAD=0.0174532925;
//定义弧度和度之间的转换比例,即弧度的值
classangle//定义类angle
{
doublevalue;//类angle的私有数据成员
public:
//类angle的公共数据成员
voidSetValue(double);
doubleGetSine();//void可以不要
}deg;//声明类angle的对象deg
voidangle:
:
SetValue(doublea)//类angle的成员函数SetValuede的具体实现
{value=a;
}
doubleangle:
:
GetSine()//类angle的成员函数GetSine的具体实现
{doubletemp;
temp=sin(ANG_TO_RAD*value);
returntemp;
}
voidmain()
{deg.SetValue(60.0);//给类angle的成员变量Value赋值
cout<<"Thesineoftheangleis:
";
cout<}
运行结果为:
Thesineoftheangleis:
0.866025
2.内联函数
调用函数时需要一定的时间,如果有的函数需要频繁使用,则所用时间会很长,从而降低程序的执行效率。
C++提供一种提高效率的方法,即在编译时将调用函数的代码嵌入到主调函数中。
这种嵌入到主调函数中的函数称为“内联函数”(inlinefunction),又称“内嵌函数”或“内置函数”。
例1-2:
Lt1-2.cpp
#include
inlineintmax(inta,intb,intc)//这是一个内联函数,求3个整数中的最大者
{if(b>a)a=b;
if(c>a)a=c;
returna;
}
voidmain()
{inti=7,j=10,k=25,m;
m=max(i,j,k);
cout<<"max="<}
运行结果为:
max=25
类的方法也可以声明和定义成内联函数。
可以使用下面两种格式定义类的内联函数:
(1)把函数原型声明和方法的定义合并,放入类定义中。
例1-3:
Lt1-3.cpp
#include
#include
constdoubleANG_TO_RAD=0.0174532925;
classangle//定义类angle
{doublevalue,temp;//定义私有数据成员
public:
doubleGetSine(doublea)//定义内联函数
{value=a;
temp=sin(ANG_TO_RAD*value);
returntemp;
}
}deg;
voidmain()
{
cout<<"Thesineoftheangleis:
";
cout<}
运行结果为:
Thesineoftheangleis:
0.866025
(2)当在函数的外部定义时,把关键字inline加在函数定义之前。
例1-4:
Lt1-4.cpp
#include
#include
constdoubleANG_TO_RAD=0.0174532925;
//定义弧度和度之间的转换比例,即弧度的值
classangle//定义类angle
{doublevalue;//类angle的私有数据成员
public:
//类angle的公共数据成员
voidSetValue(double);
doubleGetSine(void);
}deg;//声明类angle的对象deg
inlinevoidangle:
:
SetValue(doublea)//定义内联函数
{value=a;
}
inlinedoubleangle:
:
GetSine(void)//定义内联函数
{doubletemp;
temp=sin(ANG_TO_RAD*value);
returntemp;
}
voidmain()
{deg.SetValue(60.0);//给类angle的成员变量Value赋值
cout<<"Thesineoftheangleis:
";
cout<}
运行结果为:
Thesineoftheangleis:
0.866025
3.构造函数和析构函数
构造函数
在建立一个对象时,常常需要做某些初始化的工作(例如对数据赋予初值),C++提供了一种特殊的成员函数----构造函数(constructor)。
这种函数与其他成员不同,不需要用户发“消息”来激活它,而是在建立对象时自动执行。
构造函数是由用户定义的,它必须与类名同名,以便系统能识别它并把它作为构造函数。
下面是一个不带构造函数的类:
classstud//以class开头
{intnum
charname[10];
charsex;//以上3行是数据成员
voiddisplay()//这是成员函数
{cout<<”num:
”<cout<<”name:
”<cout<”sex:
”<}//以上4行是操作代码
};
studstud1;//定义了两个stud类的对象
现在我们在其中加入构造函数:
classstud//以class开头
{intnum;
charname[10];
charsex;//以上3行是数据成员
public:
stud()//定义构造函数,函数名与类名相同
{num=10010;
strcpy(name,”Wang_li”);
sex=’F’;
}//以上3行为给数据赋初值
voiddisplay()//定义成员函数
{cout<<”num:
”<cout<<”name:
”<cout<<”sex:
”<}//以上3行是操作代码
};
studstud1;//在定义对象stud1时自动执行构造函数
构造函数不需用户调用,而是在定义一个对象时由系统自动执行,而且只能执行一次。
构造函数一般声明为public,无返回值,也不需加void类型声明。
下面写成一个完整的程序:
例1-5:
建立一个对象,输出学生的学号、姓名、性别。
Lt1-5.cpp
#include
#include
voidmain()
{classstud//声明一个类
{private:
intnum;
charname[10];
charsex;//以上3行是数据成员
public:
stud()//定义构造函数,函数名与类名相同
{num=10010;//给数据赋初值
strcpy(name,"Wang_li");
sex='F';
}//以上3行为给数据赋初值
voiddisplay()//定义成员函数,输出对象的数据
{cout<<"num:
"<cout<<"name:
"<cout<<"sex:
"<}//以上3行是操作代码
};
studstud1;//在定义对象stud1时自动执行构造函数
stud1.display();//从对象外面调用display函数
}
运行结果为:
num:
10010
name:
Wang_li
sex:
F
如果要建立两个对象,分别对数据赋予初值,可将程序修改如下:
例1-6:
建立一个对象,输出学生的学号、姓名、性别。
Lt1-6.cpp
#include
#include
voidmain()
{classstud//声明一个类
{private:
intnum;
charname[10];
charsex;//以上3行是数据成员
public:
stud(intn,charnam[],chars)//定义构造函数,函数名与类名相同,有形参
{num=n;//给数据赋初值
strcpy(name,nam);
sex=s;
}//以上3行为给数据赋初值
voiddisplay()//定义成员函数,输出对象的数据
{cout<<"num:
"<cout<<"name:
"<cout<<"sex:
"<}//以上3行是操作代码
};
studstud1(10010,"Wang_li",'f'),stud2(10011,"Zhang_fun",'m');
//在定义对象stud1、stud2时自动执行构造函数,同时给出相应实参
stud1.display();
stud2.display();//从对象外面调用display函数,分别输出两个学生的数据
}
运行结果为:
num:
10010
name:
Wang_li
sex:
f
num:
10011
name:
Zhang_fun
sex:
m
析构函数
析构函数(destructor)与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。
析构函数往往用来做“清理善后”的工作,通常用于释放分配给对象的存储空间。
析构函数也是类中的特殊成员函数,与定义它的类具有相同的名字,但要在前面加上一个波浪号(~),以区别于构造函数。
析构函数没有参数,也没有返回值,而且也不能重载,因此一个类中只能有一个析构函数。
和构造函数一样,如果在类的定义中不定义析构函数,编译系统将为之产生一个默认的析构函数,对于大多数类来说,默认的析构函数就能满足要求。
若在一个对象完成其操作之前还需要做一些内部处理,则应定义析构函数。
例1-7:
包含构造函数和析构函数的C++程序。
Lt1-7.cpp
#include
#include
classstud//声明一个类
{private:
intnum;
charname[10];
charsex;//以上3行是数据成员
public:
stud(intn,charnam[],chars)//定义构造函数,函数名与类名相同,有形参
{num=n;//给数据赋初值
strcpy(name,nam);
sex=s;
}//以上3行为给数据赋初值
~stud()//定义析构函数
{cout<<"Thisisadestructor"<}
voiddisplay()//定义成员函数,输出对象的数据
{cout<<"num:
"<cout<<"name:
"<cout<<"sex:
"<}//以上3行是操作代码
};
voidmain()
{studstud1(10010,"Wang_li",'f'),stud2(10011,"Zhang_fun",'m');
//在定义对象stud1、stud2时自动执行构造函数,同时给出相应实参
stud1.display();
stud2.display();//从对象外面调用display函数,分别输出两个学生的数据
}
运行结果为:
num:
10010
name:
Wang_li
sex:
f
num:
10011
name:
Zhang_fun
sex:
m
Thisisadestructor
Thisisadestructor
在本例中,析构函数并无任何实质上的作用,只是为了说明析构函数的使用方法。
例1-8:
包含构造函数和析构函数的C++程序。
Lt1-8.cpp
#include
#include
classstud//声明一个类
{char*p;
private:
intnum;
charname[10];
charsex;//以上3行是数据成员
public:
stud(intn,charnam[],chars)//定义构造函数,函数名与类名相同,有形参
{p=newchar[50];
num=n;//给数据赋初值
strcpy(name,nam);
sex=s;
}//以上3行为给数据赋初值
~stud()//定义析构函数
{cout<<"Thisisadestructor,deletethep"<deletep;
}
voiddisplay()//定义成员函数,输出对象的数据
{cout<<"num:
"<cout<<"name:
"<cout<<"sex:
"<}//以上3行是操作代码
};
voidmain()
{studstud1(10010,"Wang_li",'f'),stud2(10011,"Zhang_fun",'m');
//在定义对象stud1、stud2时自动执行构造函数,同时给出相应实参
stud1.display();
stud2.display();//从对象外面调用display函数,分别输出两个学生的数据
}
运行结果为:
num:
10010
name:
Wang_li
sex:
f
num:
10011
name:
Zhang_fun
sex:
m
Thisisadestructor,deletethep
Thisisadestructor,deletethep
以上程序中,成员函数是在类中定义的,如果成员函数的数目很多以及函数的长度很长,类的声明就会占很大的篇幅,不利于阅读程序。
可以在类的外面定义成员函数,而在类中只用函数的原型作声明。
例1-9:
在类的外面定义成员函数。
Lt1-9.cpp
#include
#include
classstud//声明一个类
{char*p;
private:
intnum;
charname[10];
charsex;//以上3行是数据成员
public:
stud(intn,charnam[],chars);//对构造函数的原型声明
~stud();//对析构函数的原型声明
voiddisplay();//对成员函数display的原型声明
};
stud:
:
stud(intn,charnam[],chars)
//对构造函数的定义,函数名与类名相同,有形参
{p=newchar[50];
num=n;//给数据赋初值
strcpy(name,nam);
sex=s;
}
stud:
:
~stud()//对析构函数的定义
{cout<<"Thisisadestructor,deletethep"<deletep;
}
voidstud:
:
display()//对成员函数display的定义,输出对象的数据
{cout<<"num:
"<cout<<"name:
"<cout<<"sex:
"<}//以上3行是操作代码
voidmain()
{studstud1(10010,"Wang_li",'f'),stud2(10011,"Zhang_fun",'m');
//在定义对象stud1、stud2时自动执行构造函数,同时给出相应实参
stud1.display();
stud2.display();//从对象外面调用display函数,分别输出两个学生的数据
}
运行结果为:
num:
10010
name:
Wang_li
sex:
f
num:
10011
name:
Zhang_fun
sex:
m
Thisisadestructor,deletethep
Thisisadestructor,deletethep
4.拷贝构造函数
拷贝构造函数是一种特殊的构造函数,其形参是本类对象的引用。
其作用是使用一个已经存在的对象去初始化另一个同类的对象。
拷贝构造函数具有以下特点:
⑴因为该函数也是一种构造函数,所以其函数名与类名相同,并且该函数
也没有返回值类型。
⑵该函数只有一个参数,并且是同类对象的引用。
⑶每个类都必须有一个拷贝构造函数。
程序员可以根据需要定义特定的
拷贝构造函数,以实现同类对象之间数据成员的传递。
如果程序员没有定义
类的拷贝构造函数,系统就会自动生成一个缺省的拷贝构