第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();