280 likes | 366 Views
C++ 程序设计(二). 常宝宝 北京大学计算机科学与技术系 chbb@pku.edu.cn. 内容提要. C 语言中的结构 类 定义成员函数 定义对象 为类成员指定存取权限 类与面向对象的程序设计. C 语言中的结构. 在 C 语言中,可以把由多个不同类型数据组成的复杂数据类型定义为 结构 。组成结构的各个分量被称作结构的 成员 。 一旦定义了一个结构类型,可以在程序中建立结构类型变量,并通过使用 成员运算符 (.) 存取结构类型变量的各个成员。. int main() { struct _student s1, s2;
E N D
C++程序设计(二) 常宝宝 北京大学计算机科学与技术系 chbb@pku.edu.cn
内容提要 • C语言中的结构 • 类 • 定义成员函数 • 定义对象 • 为类成员指定存取权限 • 类与面向对象的程序设计
C语言中的结构 • 在C语言中,可以把由多个不同类型数据组成的复杂数据类型定义为结构。组成结构的各个分量被称作结构的成员。 • 一旦定义了一个结构类型,可以在程序中建立结构类型变量,并通过使用成员运算符(.)存取结构类型变量的各个成员。 int main() { struct _student s1, s2; s1.score = 98;//成员运算符 strcpy( s1.id, "10300506" ); strcpy( s1.name, "王二" ); s2.score = 88; strcpy( s1.id, "10300507" ); strcpy( s1.name, "陈清扬" ); …… } struct _student { char id[20];//成员 char name[20]; int score; };
类 • 为了支持面向对象的程序设计,C++提供了定义类的方法,类的概念和结构类似,不同之处在于类的成员不仅可以是数据,还可以是函数,分别称为类的数据成员和成员函数。 • 对于类中的每一个成员,C++允许指定其存取权限。 • 在定义了类后,可以建立类型为类的变量,类型为类的变量就是对象。定义对象的方法和定义结构类型变量的方法是类似的。 • 对象一旦建立,程序员也可以存取对象的各个成员,但是否可以存取要受制于定义类时指定的存取权限。存取对象成员的方式和存取结构变量成员的方式类似,使用成员运算符(.)。
类 class BankAccount { public: //存取权限 float deposit( float amount ) { //成员函数 balance+=amount; return balance; } private: //存取权限 unsigned int accountNumber; //数据成员 float balance; //数据成员 }; BankAccount a, b; //建立了类型是BankAccount的对象a,ba.deposit( 100 ); //调用对象a的成员函数
类 • 类的定义使用保留字 class • 类的定义意味着——定义数据成员定义成员函数指定成员的存取权限 • 定义类的基本格式class 类的名字 { … };//不要忘记这个分号
定义类的数据成员 • 在类中定义数据成员和在C的结构中定义(数据)成员的方法相同。如:class TDate { …… int year; int month; int day; …… };
定义类的成员函数 • 类的成员函数定义了该类所能提供的功能。 • 定义成员函数的方法和普通(全局)函数类似,但不完全相同。 • 例子日期类TDate的对象代表一个日期,和日期有关的操作是: 初始化日期(设定日期的初值); 把日期按照指定的格式显示出来; 判断该日期的年份是否闰年;……
在类的内部定义成员函数 • 定义成员函数,有两种选择,一种是在类内定义,一种是在类外定义。 • 如果类的成员函数比较简单,可以选择在类中进行定义。 class TDate { …… void Set( int m, int d, int y ) { month = m; day = d; year = y; } …… int year; int month; int day; }; 在类内定义成员函数和定义普通函数的形式完全相同。但函数要放在类定义的内部。 类的成员函数使用本类的数据成员,可直接使用,无需使用成员运算符(.)。
在类的内部定义成员函数 class TDate { public: void Set( int m, int d, int y ) { month = m; day = d; year = y; } int IsLeapYear() {//判断一个年份是否闰年 return ( year%4==0 && year%100!=0 )||( year%400==0 ); } void Print() { cout << month << "/" << day << "/" << year << endl; } int year; int month; int day; };
在类的内部定义成员函数 • 在类中定义的成员函数一般比较简单,编译器一般将其处理为内联函数,既使没有使用保留字 inline。 • 类通常放在头文件中进行定义,如果在类中进行定义成员函数,成员函数代码会泄漏。 • 如果在类中定义成员函数过多,类定义和成员函数定义混在一起,很不清晰,不利于程序的阅读。
在类的外部定义成员函数 • C++允许在类的外部定义成员函数,这样完整的类定义分成两个部分,一部分是类定义,这一部分放在头文件(H文件)中;另一部分是成员函数的定义,这部分通常放在cpp文件中。 • 如果在类的外部定义成员函数,此时类定义中只需定义数据成员及列出该类的成员函数原型。 // tdate.h class TDate { public: void Set( int, int, int ); int IsLeapYear(); void Print(); int year; int month; int day; }; 在类的外部定义成员函数,程序显得结构清晰,类提供什么样的功能?只要看类的定义就很清楚。如果关心这些功能是怎样实现的,再去看成员函数的定义。
在类的外部定义成员函数 • 在类的外部定义成员函数时,不同之处在于应在成员函数名前增加作用域指示符,指明该成员函数隶属于哪个类,格式如下: 返回类型 类名::成员函数名(形式参数表) { ...... } int TDate::IsLeapYear() {//注意类名应该加在成员函数名的前面 return ( year%4==0 && year%100!=0 )||( year%400==0 ); } 如果没有作用域指示符,成员函数就会被编译器误以为是一个普通的函数。 为什么在类内定义成员函数时无需作用域指示符?一班有个同学叫张飞、二班也有个同学叫张飞
在类的外部定义成员函数 //tdate.cpp #include <iostream.h> #include "tdate.h“ void TDate::Set( int m, int d, int y ) { month = m; day = d; year = y; } int TDate::IsLeapYear() { return ( year%4 == 0 && year%100!=0 )||(year%400==0 ); } void TDate::Print() { cout << month << "/" << day << "/" << year << endl; }
定义类的对象 • 在C++中,可以在程序中定义类的对象,就象建立结构类型的变量,例如: TDate a; //a是类TDate的对象。 • 在定义了该类的对象后,可以访问对象的数据成员以及调用该类对象的成员函数,这时要使用成员运算符(.),要同时指明对象名和成员名。 a.Set(9,27,2003); //调用对象的成员函数 a.Print(); //调用对象的成员函数 a.year = 2004; //给对象的数据成员赋值 a.month = 11; //给对象的数据成员赋值
a.Set(9,27,2003) 定义类的对象 • 定义某类的对象意味着在内存中分配一片存储空间存储对象的各个数据成员。 • 调用对象的成员函数或存取对象的数据成员有可能改变对象的状态(即对象数据成员的值)。
定义类的对象 • 不但可以定义某类的对象,也可以定义指向某类对象的指针,此时存取对象的成员要使用指针成员运算符(->)。TDate a, *pa; pa = &a; pa->Set(9,27,2003); pa->Print(); • 可以定义某类对象的引用,此时应注意初始化,此时存取对象成员仍然使用成员运算符(.)。TDate a; TDate& ra = a; ra.Set(9,27,2003); ra.Print();
类及其客户 • 如果某个普通函数或另外一个类中用到了类A,该函数或类可以称为类A的客户。如:int main() { TDate a; ... a.Set(9,27,2003); a.Print(); ... } 在main函数中定义了TDate类的对象a,此时main函数就成为TDate类的客户。
类成员的存取权限 • 然而,客户调用对象的成员函数和存取对象的数据成员是受到控制的,如果没有为类中的成员指定存取权限,则客户既不能调用其成员函数,也不能访问其数据成员。 a.Set(9,27,2003); a.Print(); a.year = 2004; a.month = 11;
类成员的存取权限 • C++中定义了三种成员存取控制指示符,分别是public、protected 和 private。 • 在一个类中,一个存取控制指示符可以为其后面紧跟着的一组成员设定存取控制权限,形式如下:存取控制指示符: 成员1; 成员2; ... 成员n; class T { …… private://别忘了冒号 int m1; float m2; void func(); …… }
类成员的存取权限 • 存取权限是public的成员称为类的公共成员。客户可以存取类的所有公共成员。 • 存取权限是private的成员称为类的私有成员。客户不可以存取类的私有成员。 • 存取权限是protected的成员称为类的保护成员。客户不可以存取类的保护成员。 • 如果类的某个成员被指定为私有成员或保护成员,虽然类的客户不能直接存取该成员,但该类的所有成员函数均可以直接存取。 • 如果在类中没有指定某个成员的存取权限,则该成员是私有成员。
类成员的存取权限 // tdate.h class TDate { public: void Set( int, int, int ); int IsLeapYear(); void Print(); private: int year; int month; int day; }; int main() { TDate a; ... a.Set(9,27,2003); a.Print(); a.year = 2003;// 错误 a.month = 10; // 错误 a.day = 30; // 错误 ... } • year、month、day是私有成员,客户不能直接存取。 • Set(…)、Print()、IsLeapYear()是公共成员,客户可以直接存取。
类成员的存取权限 // tdate.h class TDate { public: void Set( int, int, int ); int IsLeapYear(); void Print(); int year; int month; int day; }; int main() { TDate a; ... a.Set(9,27,2003); a.Print(); a.year = 2003;// 正确 a.month = 10; //正确 a.day = 30; //正确 ... } • 客户可以直接存取所有成员,因为6个成员均为公共成员。
class X { public: int m1; int m2; protected: int m3; int m4; public: //第二次出现了public int f1(); int f2(); int m5; private: int f3(); int m6; private: //可以这样指定,但多余 int f4(); int m7; }; 类成员的存取权限 • 类中可以出现的成员存取权限指示符数量不限,顺序不限。 • 类X中定义了11个成员,其中4个是私有成员,5个公共成员,2个保护成员。
C++中的结构 • C++中的结构struct和C中的struct不同,实际上C++的中的struct和class一样,在C++中,也可以为结构定义成员函数,惟一不同在于默认的存取权限,class中若没有指定成员的存取权限,则默认为private成员,而struct中没有指定成员存取权限,默认是public成员。 struct TDate { void Set( int, int, int ); int IsLeapYear(); void Print(); private: int year; int month; int day; };
类和面向对象的程序设计 • 类把数据和针对数据的操作(成员函数)封装在一起。因为数据和操作数据的算法是不可分的。 • 类中存取控制,使得类可以把类实现的细节隐藏起来。类的所有公共成员构成了类的接口,客户只须了解类的接口即可使用类。类的作者可以随时修改类的实现,只要不改变类的接口就不影响类的使用。 • 类可以作为代码重用的基本单位。
类和面向对象的程序设计 • 类的引入改变了程序员求解问题的思路。 现实世界中对象和程序设计语言中的对象有很好的对应关系。 • 类的引入也改变了人们对程序的理解。程序 = 算法 + 数据结构程序 = 对象 + 对象 + … + 对象对象 = 算法 + 数据结构
上机练习内容 • 在机器上练习本节介绍的内容。 • 《C++程序设计教程》p.257练习11.1 、11.2和11.3