第5章类和对象(一)
(2008-06-09 08:41:04)
转载
标签:
构造函数
day
date
数据类型
it
分类:c++学习相关
第5章类和对象
5.1 结构
C++具有丰富的基本数据类型,如:char、int、double等等,使用这些数据类型,已经可以解决很多问题了。
但是,在某些场合,数据类型的集合可以更方便地解决某些问题。例如:数组,它把若干相同类型的数据集合在一起,解决了成批同类型数据的处理问题。
对于由 “若干相同类型的数据集合”构成的数组,由于每个元素的类型都相同,因此,其数组的“整体类型”也就是这个公共的类型了。所以,其处理过程也比较简单,数组中的每一个元素等价于同类型的一个变量,使用下标方法就可以方便地处理数组元素。
在实际问题中,我们往往会碰到“一组数据中的各个数据具有不同类型”的情况。例如,例如,对于处理一个班的学生成绩问题时,每个人都涉及到“姓名、性别、年龄、成绩”的问题,这一组组的数据都有内在联系且类型不同,姓名应为字符型数组,性别应为字符型;年龄应为整型;成绩可为整型或实型。显然不能用一个数组来存放这一组数据。
对于这样的由“若干不同类型的数据集合”而构成的整体,C++称之为结构。结构中,各成员的类型不一定相同,那么该如何表达其“整体类型”呢?
C++提供了一种自定义数据类型的机制,即允许程序员自己创造数据类型。这种可以自定义数据类型的方法,可以为我们解决复杂问题提供了方便。
C++内部数据类型的名称全是由小写字母构成的,为了与之区别,我们一般将自定义数据类型的名称以大写字母开头。
一、结构类型的定义
定义某个特定结构类型,采用以下一般形式:
struct 结构名
{
类型说明符 成员名1;
类型说明符 成员名2;
类型说明符 成员名3;
类型说明符 成员名4;
…… ……
};
例如:
struct Stu
{
char name[20];
char sex;
int age;
float score;
};
注意:在括号后的分号是不可少的。
结构类型定义好之后,即可用这个类型去进行变量说明。这时,被说明的变量,是由数目固定的有序变量的集合,结构中的每一个变量的类型可能是不相同的。由此可见,结构是一种复杂的数据类型。
二、结构类型变量的说明
说明结构变量有以下三种方法。以上面定义的stu为例来加以说明。
1)先定义结构,再说明结构变量。这是最常用的方法,如:
struct Stu
{
char name[20];
char sex;
int age;
float score;
};
Stu boy1,boy2;
说明了两个变量boy1和boy2为stu结构类型。
2)在定义结构类型的同时说明结构变量。例如:
struct Stu
{
char name[20];
char sex;
int age;
float score;
}boy1,boy2;
3)直接说明结构变量。例如:
struct
{
char name[20];
char sex;
int age;
float score;
}boy1,boy2;
成员也可以又是一个结构,即构成了嵌套的结构。例如:
struct Date
{
int month;
int day;
int year;
};
Struct
{
char name[20];
char sex;
Date birthday;
double score;
}boy1,boy2;
三、结构变量中成员名的使用
1、成员名可与程序中其它变量同名,互不干扰。
2、表示结构变量成员的一般形式是:结构变量名.成员名
例如:boy1.num 即第一个人的学号 boy2.sex即第二个人的性别。
3、如果成员本身又是一个结构则必须逐级找到最低级的成员才能使用。
例如:boy1.birthday.month 即第一个人出生的月份
4、给结构变量的赋值就是给各成员赋值。可用输入语句或赋值语句来完成。
例题:日期结构
#include<iostream.h>
struct Date
{
int year;
int month;
int day;
};
void print(Date);
bool isLeapYear(Date d);
void main()
{
Date d;
d.year = 2000;
d.month = 12;
d.day = 6;
if(isLeapYear(d)) print(d);
}
void print(Date s)
{
cout<<s.year<<'-'<<s.month<<'-'
<<s.day<<endl;
}
bool isLeapYear(Date d)
{
return(d.year%4==0&&
d.year%100!=0)||(d.year%400==0);
}
结构类型的定义还是存在局限性,它只定义了一组不同类型数据的集合,而没有定义相关操作,如赋值、输出等。特别是在一个程序员接手另一个程序员工作的时候,这种对结构的理解和编写相关操作的负担加重了。
为了减轻其它程序员的编程负担,我们需要把结构类型的定义做得更全面些,使它不仅仅有数据的定义,而且有操作的定义,这就是类。
5.2 类与对象
一、什么是类
类是一种由程序员自己创造的复杂数据类型。具体说:类是一个数据类型;类类型通常很复杂;类类型是由程序员自己创造的。
程序员将各种彼此相关的各种类型的数据和与这些数据相关的操作(函数)封装在一起的,便创造了一个具体类类型。
通俗地说,类类型中包含了若干数据,还包含了若干函数。
二、类类型的定义
采用C++提供的自定义数据类型的机制,由程序员自己创造数据类型的方法,完成类类型的定义。
类类型的定义一般又两部分构成:说明部分和实现部分。
说明部分是用来说明该类中的成员,包含数据成员的说明和成员函数的说明。成员函数是用来对数据成员进行操作的,又称为“方法”。
实现部分是用来对成员函数的定义。
概括说来,说明部分将告诉使用者“干什么”,而实现部分是告诉使用者“怎么干”。
1、类的一般定义格式如下:
class 类名
{
public:
数据成员或成员函数的说明
private:
数据成员或成员函数的说明
protected
数据成员或成员函数的说明
};
各个成员函数的实现
2、定义类时应注意的事项
A、class是定义类类型的关键字,“类名”是标识符,通常用大写字母开始的字符串作为类名。
B、花括号内是类的说明部分,说明该类包含哪些数据成员和哪些成员函数。
C、类中的数据成员的类型可以是任意的,包含整型、浮点型、字符型、数组、指针和引用等。
D、另一个类的对象,可以作该类的成员。当一个类的对象为这个类的成员时,如果另一个类的定义在后,需要提前说明。
E、在类体中不允许对所定义的数据成员进行初始化,因为类的定义只是在创造一个类型而已,而不是在说明“变量”。
3、类成员的访问控制
A、从访问权限上来分,类的成员又分为:公有的(public)、私有的(private)和保护的(protected)三类。
B、公有成员用public来说明,公有部分往往是一些操作(成员函数),它是提供给用户的接口。这部分成员可以在程序中引用。
C、私有成员用private来说明,私有部分通常是一些数据成员,这些成员是用来描述该类中的对象的属性的,只有成员函数或经特殊说明的函数才可以引用它们,它们是被用来隐藏的部分。
D、保护成员用protected来说明,在大多数情况下,类的保护成员具有私有成员的性质,但在派生类的成员函数而言,它具有公有成员的性质。
关键字public、private和protected被称为访问权限修饰符或访问控制修饰符,用它们来说明类成员的访问权限。它们在类体内出现的先后顺序无关,并且允许多次出现。
一般地,在类体内先说明公有成员,它们是用户所关心的,后说明私有成员,它们是用户不感兴趣的。在说明数据成员时,一般按数据成员的类型大小,由小至大说明,这样可提高时空利用率。
经常习惯地将类定义的说明部分或者整个定义部分(包含实现部分)放到一个头文件中。
4、类成员函数的实现
在类类型的定义中,“各个成员函数的实现”是类定义中的实现部分,这部分包含所有在类体内说明的函数的定义。
返回值的类型 类名::成员函数名(参数表)
{
函数体
}
其中“::”叫作用域运算符。
如果一个成员函数的类体内定义了,实现部分将不出现。如果所有的成员函数都在类体内定义,则实现部分可以省略。
例题:日期类的定义(说明部分+实现部分)
class Date
{
public:
void set(int y,int m,int d);// 赋值操作
boolisLeapYear(); // 判断闰年
voidprint(); // 输出日期
private:
int year,month,day;
};
void Date::set(int y,int m,int d)
{
year=y;
month=m;
day=d;
}
bool Date::isLeapYear()
{
return(year%4==0&&year%100!=0)||
(year%400==0);
}
void Date::print()
{
cout<<year<<'-'<<month<<'-'<<day<<endl;
}
三、对象
回忆变量的定义方法:double x,y,z;
1、类似于用基本数据类型来说明变量的方法,我们可以用自己创建的类类型来说明“变量”,此时的“变量”叫对象。
2、访问对象的公有成员:
当用自己创建的类类型来说明“对象”以后,程序中就可以用运算符“.”来引用其公有成员,引用的方式如下:
对象名.公有数据成员名
对象名.公有成员函数名(实参表)
对“对象”中数据成员的赋值和数据传递一般都是通过成员函数来进行的。
#include<iostream.h>
class Date
{
public:
void set(int y,int m,int d);// 赋值操作
boolisLeapYear(); // 判断闰年
voidprint(); // 输出日期
private:
int year,month,day;
};
void Date::set(int y,int m,int d)
{
year=y;
month=m;
day=d;
}
bool Date::isLeapYear()
{
return(year%4==0&& year%100!=0)
||(year%400==0);
}
void Date::print()
{
cout<<year<<'-'<<month<<'-'<<day<<endl;
}
void main()
{
Date d;
d.set(2000,12,6);
if(d.isLeapYear())
d.print();
}
指向对象的指针的成员表示如下:
对象指针名->公有数据成员名
对象指针名->公有成员函数名(实参表)
注意:->用于表示指向对象的指针的成员
. 用于表示一般对象的成员
下列表示是等价的:
对象指针名->公有数据成员名
(*对象指针名).公有数据成员名
5.3 对象的初始化
一、构造函数和析构函数
1、什么是构造函数
在进行类的定义时(程序员自己创建类类型),不能给类中数据进行初始化,因为在这一过程中,仅仅是创建一个类类型而已。就好象系统在创造int作为整型的时候,它也不能先给整型数据进行初始化。
我们可以使用系统的基本数据类型来说明变量,我们也可以使用自己创建的类类型来创建对象。
在使用系统的基本数据类型来说明变量时,可以对该变量进行初始化,如:intx=5;
当我们使用自己创建的类类型来创建对象时,可不可以采用类似的方式进行初始化呢?回答是肯定的。
要求:对象的初始化工作须通过构造函数来完成。
2、构造函数的特点:
1)构造函数是类的一个成员函数,但有其特殊性。
2)构造函数的函数名与类名相同。
3)构造函数不能有返回值。
4)构造函数的主要功能是完成对象的初始化工作。
5)构造函数可以重载。
在使用类名创建对象的时候,系统会自动调用构造函数,实现其“完成对象的初始化工作”的功能。
回忆例题:
#include<iostream.h>
class Date
{
public:
void set(int y,int m,int d);// 赋值操作
boolisLeapYear(); // 判断闰年
voidprint(); // 输出日期
private:
int year,month,day;
};
void Date::set(int y,int m,int d)
{
year=y;
month=m;
day=d;
}
bool Date::isLeapYear()
{
return(year%4==0&& year%100!=0)
||(year%400==0);
}
void Date::print()
{
cout<<year<<'-'<<month<<'-'
<<day<<endl;
}
void main()
{
Dated; //创建对象
d.set(2000,12,6);
//调用set函数实现对象初始化
//注意私有数据是不好直接赋值的
if(d.isLeapYear())
d.print();
}
使用构造函数改写以上程序
#include<iostream.h>
class Date
{
public:
Date(int y,int m,int d);
//构造函数,与类同名,不能指定返回值
boolisLeapYear(); // 判断闰年
voidprint(); // 输出日期
private:
int year,month,day;
};
Date::Date(int y,int m,int d)
{
year=y;
month=m;
day=d;
}
bool Date::isLeapYear()
{
return(year%4==0&& year%100!=0)
||(year%400==0);
}
void Date::print()
{
cout<<year<<'-'<<month<<'-'
<<day<<endl;
}
void main()
{
Date d(2000,12,6);
//创建对象时,自动调用构造函数
if(d.isLeapYear())
d.print();
}
1、什么是析够函数
析构函数也是特殊的类成员函数,它没有返回类型,没有参数,也没有重载,不能随意调用,只有在类对象的生命期结束的时候,由系统自动调用。
1)析够函数是类的一个特殊成员函数。
2)析够函数的函数名取“~类名”。
3)析够函数不能有返回值,也不能有函数参数。
4)析够函数的主要功能是在对象消失时,执行如释放内存等清理工作。
5)在对象消失时,析够函数会被自动调用。
例题:
#include<iostream.h>
class Date
{
public:
Date(int y,int m,int d); // 构造函数
~Date(); // 析构函数
voidprint(); // 输出日期
private:
int year,month,day;
};
Date::Date(int y,int m,int d)
{
year=y;
month=m;
day=d;
cout<<"Constructorcalled."<<endl;
}
Date::~Date()
{
cout<<"Destructorcalled"<<endl;
}
void Date::print()
{
cout<<year<<'-'<<month<<
'-'<<day<<endl;
}
void main()
{
Date today(2007,10,17);
Date tomorrow(2007,10,18);
cout<<"today is";
today.print();