50
第第第 类类类类 II 类类类类类类

第十讲 类与对象(二)

Embed Size (px)

DESCRIPTION

第十讲 类与对象(二). 内容提要. 对象的生存期 静态成员 友元 常对象 对象数组与对象指针 向量类: vector 字符串类: string. 生存期与作用域. 对象生存期: (与普通变量相同). 静态生存期:生存期与程序的运行期相同,即一直有效 动态生存期:当对象所在的程序块执行完后即消失. 全局对象:静态生存期 动态对象:动态生存期. 类的数据成员的作用域(称为 类作用域 ). 数据成员可以被类中的所有函数成员访问(类似全局变量) 成员函数中声明的变量是局部变量 - PowerPoint PPT Presentation

Citation preview

Page 1: 第十讲    类与对象(二)

第十讲 类与对象

( II )面向对象进阶

Page 2: 第十讲    类与对象(二)

2

内容提要 对象的生存期 静态成员 友元关系 常对象与常成员 对象数组与对象指针 向量类: vector

字符串类: string

Page 3: 第十讲    类与对象(二)

3

静态生存期:生存期与程序的运行期相同,即一直有效 动态生存期:当对象所在的程序块执行完后即消失

与普通变量一样,对象有静态和动态生存期

全局对象:静态生存期 动态对象:动态生存期

对象的生存期对象的生存期

Page 4: 第十讲    类与对象(二)

4

成员作用域

类的数据成员的作用域(称为类作用域) 数据成员可以被类中的所有函数成员访问(类似全局变

量) 成员函数中声明的变量是局部变量 如果成员函数中声明了与数据成员同名的变量,则数据成

员被屏蔽

Page 5: 第十讲    类与对象(二)

5

例:数据成员作用域class Point //Point 类的声明{ public: // 外部接口

Point(int a=0, int b=0) { x=a; y=b;} int Getx() {return x;}int Gety() {return y;}int p() { cout << x << endl; int x=5; cout <<

x;} private: // 私有数据

int x, y;}; int main(){ Point A(4,5), B(8,0); A.p(); B.p(); return 0; }

ex10_class_01.cpp

Page 6: 第十讲    类与对象(二)

6

为什么要静态 静态数据成员 静态函数成员 关键字 static

静态成员静态成员

Page 7: 第十讲    类与对象(二)

7

类的静态成员

静态数据成员和静态函数成员一般情况下,同一个类的不同对象分别有自己的数据成员,名字一样,但各自有值,互不相干。但有时希望某些数据成员为所有对象所共有,这样可以实现数据共享。全局变量可以达到共享数据的目的,但其安全性得不到保证:任何函数都可以自由修改全局变量的值,很有可能偶然失误,全局变量的值被错误修改,导致程序的失败。因此在实际工作中尽量少使用全局变量。

如果需要在同一个类的多个对象之间实现数据共享,可以使用静态数据成员。

Page 8: 第十讲    类与对象(二)

8

静态数据成员

用关键字 static 修饰 该类的所有对象共同使用和维护该成员 静态变量可以初始化,但必须在类外初始化

静态数据成员

class Point { public: ... ... private: int x, y; static int count; // 引用性声明};

int Point::count=0; // 静态数据成员的定义和初始化... ...

例:

Page 9: 第十讲    类与对象(二)

9

静态数据成员

静态数据成员为整个类所共有,不属于任何特定对象 静态数据成员在内存中只占一份空间 只要在类中定义了静态数据成员,即使不定义对象,

也为静态数据成员分配空间,它可以被引用 如果静态数据成员没有初始化,则系统会自动赋予初值 0

静态数据成员既可以通过对象名引用,也可以通过类名来 引用,即:对象名 . 静态成员名 或 类名 :: 静态成员名

ex10_class_03.cpp

Page 10: 第十讲    类与对象(二)

10

静态函数成员

用关键字 static 修饰,为整个类所共有 调用方式:类名 :: 静态函数成员名 没有目的对象,所以不能对非静态成员进行默认访问 静态成员函数一般用于访问静态数据成员,维护对象

之间共享的数据

静态函数成员

class A { public: ... ... static void fun(A a); ... ...};

例:

实际上也允许通过对象名调用静态成员函数,但此时使用的是类名,而不是对象。

静态成员函数声明时需加 static ,但定义时不能加 static 。

Page 11: 第十讲    类与对象(二)

11

静态函数成员 静态成员函数可以直接访问静态成员,但不能直接访问非

静态成员。

ex10_class_04.cpp

// 静态成员函数中有以下语句:cout << height; // 若 height 是 static ,则合法cout << width; // 若 width 是非静态数据成员,则不合法

如果静态成员函数访问非静态数据成员时,需指明对象cout << p.width; // 访问对象 p 的非静态数据成员 width

编程好习惯:只用静态成员函数引用静态数据成员,而不引用非静态数据成员,这样思路清晰,逻辑清楚,不易出错

Page 12: 第十讲    类与对象(二)

12

友 元 什么是友元关系 为什么要定义友元 友元函数、友元类 关键字 friend

Page 13: 第十讲    类与对象(二)

13

类的友元

友元提供了一种数据共享的方式,提高了程序效率和可读性 但友元在一定程度上破坏了数据封装和数据隐藏的机制

友元关系:提供一种不同类或对象的函数成员之间、类的函数成员与一般函数之间进行数据共享的机制

通俗说法:一个类主动声明哪些类或函数是它的朋友,从而给它们提供对本类的访问特许,即可以访问私有成员和保护成员

友元包括:友元函数与友元类 友元类的所有函数成员都是友元函数

Page 14: 第十讲    类与对象(二)

14

友元函数

用关键字 friend 修饰 可以是普通函数或其它类的成员函数 友元函数可以通过对象名直接访问私有成员和保护成员

友元函数

class Point { public: ... ... friend float dist(Point & p1, Point & p2); private: int x, y;};float dist(Point & p1, Point & p2) { double x=p1.x-p2.x, y=p1.y-p2.y; return sqrt(x*x+y*y); }... ...

例:

Page 15: 第十讲    类与对象(二)

15

友元类

用关键字 friend 修饰 友元类的所有函数成员都是友元函数

友元类

class A { public: ... ... friend class B; // 声明 B 是 A 的友元类 ... ...};

除非确有必要,一般并不把整个类声明为友元类,而只将确实有需要的成员函数声明为友元函数,这样更安全。

Page 16: 第十讲    类与对象(二)

16

友元类 关于友元关系的几点说明

友元关系不能传递 友元关系是单向的 友元关系不能被继承

面向对象程序设计的一个基本原则是封装性和信息隐蔽,而友元是对封装原则的一个小的破坏。但是它能有助于数据共享,提高程序的效率。在使用友元时,要注意它的副作用,不要过多地使用友元,只有在使用它能使程序精炼,并能大大提高程序的效率时才用友元,否则可能得不偿失。

Page 17: 第十讲    类与对象(二)

17

常对象与常成员 为什么需要常对象 常对象的声明 常数据成员、常函数成员 常引用 关键字 const

Page 18: 第十讲    类与对象(二)

18

常对象

用关键字 const 修饰 常对象必须进行初始化(参数初始化表,不能赋值)

将对象声明成常对象,可以有效地保护数据

const 类名 对象名;类名 const 对象名;

常对象的值不能被改变 — 数据成员:均为常量,不能被赋值 — 函数成员:不能通过常对象调用普通成员函数!

声明一个对象为常对象,只针对该对象的数据成员。

Page 19: 第十讲    类与对象(二)

19

常成员

若一个对象是常对象,则通过该对象只能调用常成员函数 无论对象是否为常对象,在常成员函数被调用期间,目的对

象都将被视为常对象

类型说明符 函数名 (形参 ) const;

常函数成员

常数据成员 将数据成员声明为常量 必须初始化(采用参数初始化表,不能在构造函数内赋

值)// 假定数据成员 a 和 b 是常量, c 是普通变量Myclass::Myclass(int x, int y, int z): a(x),b(y){ c=z; }

Page 20: 第十讲    类与对象(二)

20

常引用

常引用

常引用所引用的对象不能被更新 常引用可以绑定到常对象(普通引用不能)

const 类型说明符 & 引用名;

在 C++ 编程中,经常用常指针和常引用作函数参数,这样既可以节省存储量和运算时间,又能保证数据的安全。

Page 21: 第十讲    类与对象(二)

21

对象数组与对象指针 对象数组的声明与初始化 指向对象的指针、 this 指针 指向成员的指针 创建动态对象 特殊符号: ->

关键字: new 、 delete

Page 22: 第十讲    类与对象(二)

22

对象数组 一维对象数组的声明

类名 数组名 [n]

一维对象数组的引用数组名 [k]. 成员名

初始化:对每个元素都调用构造函数

... .. Point() { x=0; y=0} Point(int a, int b) { x=a; y=b; }... ...int main(){ Point A[2]={Point(), Point(2,3)}; ... ...

例:

ex10_class_04.cpp

Page 23: 第十讲    类与对象(二)

23

对象指针对象指针:指向对象的指针,存放对象的地址

类名 * 对象指针名

使用对象指针访问对象成员:“ -> ”

对象指针名 -> 成员名

Point a(1,2);Point * pa = &a;

例:

也可以使用普通方式,即: (* 对象指针名 ). 成员名

对象指针的声明

指向常对象的指针必须是常指针(声明时加 const )

Page 24: 第十讲    类与对象(二)

24

this 指针this 指针:隐含在非静态成员函数中的特殊指针,指向目的对象

this 指针是常指针 当局部作用域中声明了与类成员同名的标识符(如变量名)

时,可以通过 this 指针访问该类的成员

当通过一个对象的调用成员函数 ( 非静态成员函数 ) 时,系统会把该对象的起始地址赋给成员函数的 this 指针。

非静态成员函数有 this 指针,而静态成员函数没有 this 指针

Page 25: 第十讲    类与对象(二)

25

例: this 指针class Point //Point 类的声明{ public: // 外部接口

Point(int a=0, int b=0) { x=a; y=b;} int Getx() {return x;}int Gety() {return y;}int p() {int x=5; cout << x;

cout << this->x << endl; }void Setx(int x) { this->x=x; }

private: // 私有数据int x, y;

}; ex10_class_05.cpp

Page 26: 第十讲    类与对象(二)

26

指向成员的指针指向成员指针:直接指向类的成员的指针

指向非静态成员的指针

类型说明符 类名 ::* 指针名 // 指向数据成员类型说明符 ( 类名 ::* 指针名 )(参数 ) // 指向函数成员

指向静态成员的指针

对类的静态成员的访问不依赖于对象,可以通过普通的指针来指向和访问静态成员

Page 27: 第十讲    类与对象(二)

27

指向非静态成员

函数成员指针的赋值与引用

指针名 = & 类名 :: 函数成员名

( 对象名 .* 指针名 )(参数 )( 对象指针名 ->* 指针名 )(参数 )

数据成员指针的赋值与引用

指针名 = & 类名 :: 数据成员名

对象名 .* 指针名对象指针名 ->* 指针名

Page 28: 第十讲    类与对象(二)

28

创建动态对象

动态对象的释放

delete 指针名

类名 * 指针名 =new 类名 () // 不带参数类名 * 指针名 =new 类名 (参数列表 ) // 带参数

程序结束后,动态对象也会被释放。

Page 29: 第十讲    类与对象(二)

29

向量类 vector

向量类,头文件 vector

向量对象 向量操作 关键字 vector

Page 30: 第十讲    类与对象(二)

30

向量类C++ 提供了向量类,使用向量与使用数组一样,但向量的长度可以根据需要自动增减,比普通数组更灵活。

向量的声明vector<基本数据类型说明符 > 向量名

#include <vector> ... ... vector<int> x; // 声明一个整型向量

例:

可创建不同类型的向量,这里 x 是对象,不是普通数组

向量的所有元素都会被初始化:

需要加库文件 vector

- 若是基本数据类型,则全为零- 若是某个类的对象,则调用相应构造函数初始化

Page 31: 第十讲    类与对象(二)

31

向量类 向量类构造函数

vector<Type>(); // 缺省构造函数,创建一个空向量vector<Type>(int n); // 创建长度为 n 的向量 vector<Type>(int n, Type x); // 创建长度为 n 的向量,并用 x 初始化所有分量 这里的 Type 是基本数据类型,如 int , float , double 等

vector<int> x(100); // 创建长度为 100 的整型向量vector<float> y(10, 2.1); // 创建长度为 10 的单精度型向量,初值都是 2.1

vector<double> z(); // 创建一个双精度型空向量

例:

Page 32: 第十讲    类与对象(二)

32

基本操作 向量的基本操作v[k] 第 k 个分量,下标从 0 开始v1 = v2 赋值(复制)v1 == v2 个数和值都相等时返回真v1 != v2 不相等时返回真<, <=, >, >= 按字典顺序进行比较

Page 33: 第十讲    类与对象(二)

33

常用成员函数at(int k) 返回下标为 k 的分量size() 返回向量的长度clear() 清空向量中的数据empty() 判断向量是否为空(长度为 0 )front() 返回第一个分量的值back() 返回最后一个分量的值push_back( 数据 )

在向量末尾插入数据pop_back() 删除最后一个分量swap(vector) 交换向量的值

关于 vector 的更多介绍参见相关资料

向量名不是地址!向量是对象ex10_vector.cpp

Page 34: 第十讲    类与对象(二)

34

字符串类 string

字符串对象与字符数组 string 类的构造函数 字符串各种操作 关键字 string

Page 35: 第十讲    类与对象(二)

字符串类字符串:可以通过字符数组实现,也可以通过 string 类实现

C++ 的 string 类提供了处理字符串的各种函数, 使得 C++ 的字符串操作更加方便

使用 string 类必须包含 string 头文件

#include <string> // 注意不是 cstring ... ...

string str; // 定义一个空字符串对象string str1="Math.", str2("ECNU"); // 可以初始化string str3=str1+str2; // str3="Math.ECNU"

string str4(5,'c'); // 连续 5 个字符 c

例:

为了以示区别 , 后面将由字符数组定义的字符串称为数组字符串

Page 36: 第十讲    类与对象(二)

36

字符串类 string 类构造函数原型

string(); // 默认构造函数string(const string & s); // 复制构造函数string(const char * s); // 用字符串常量初始化string(const string & s, unsigned int p, unsigned int n); // 从位置 p 开始,取 n 个字符,即 s[p], .., s[p+n-1]

string(const char * s, unsigned int n); // 使用前 n 个字符string(unsigned int n, char c); // 将给定的字符重复 n次

Page 37: 第十讲    类与对象(二)

37

输入输出 string 对象的输入输出

cin >> str

cout << str

getline(cin,str) // 以换行符作为输入结束符getline(cin,str,'c') // 将字符 'c' 作为输入结束符

输入 getline

Page 38: 第十讲    类与对象(二)

38

基本操作操作符 示例 功能

+=+===!=<<=>>=[ ]

str1 + str2str1 = str2str1 += str2str1 == str2str1 != str2str1 < str2str1 <= str2str1 > str2str1 >= str2str[i]

连接两个字符串用 str2 更新 str1, 即复制str1 = str1 + str2

比较字符串大小比较字符串大小比较字符串大小比较字符串大小比较字符串大小比较字符串大小访问下标为 i 的字符 , 数组方式 以上操作通过操作符重载实现

比较大小按字典顺序,从前往后逐个进行比较

Page 39: 第十讲    类与对象(二)

39

基本操作举例string str1="Hello";string str2(3,'A');string str3 = str1 + " Math";

两个 string 对象可以相加 string 对象和数组字符串也可以相加 但两个数组字符串不能直接相加

string str4="Hello" + " Math"; // ERROR

string str4="Hello" + str2 + " Math"; // OK

ex10_string.cpp

Page 40: 第十讲    类与对象(二)

40

基本操作举例 string 对象可以直接赋值 但数组字符串不能!

string str1;char str2[10];

str1="Hello"; // OKstr2="Hello"; // ERROR

Page 41: 第十讲    类与对象(二)

41

更多操作str[k] 返回第 k 个字符str.at(k) 返回第 k 个字符 ( 会自动检测是否越界 )

str.length() 字符串对象的长度(字符个数)str.size() 同上str.capacity() 返回为当前字符串分配的存储空间

str.clear() 清除字符串中所有内容str.erase(k,n) 从下标 k 开始 , 连续清除 n 个字符str.~string() 释放 string 对象str.empty() 判断 string 对象是否为空

是 (true) ,否 (false)

Page 42: 第十讲    类与对象(二)

42

更多操作str.assign(str) 用字符串对象赋值str.assign(str[]) 用数组字符串赋值str.assign(str,k,n)

用 str 从下标 k 开始的连续 n 个字符赋值

str.assign(str,n) 用 str 前 n 个字符赋值str.assign(n,c) 用 n 个字符 c 赋值string str1, str2, str3, str4, str5;

str1.assign("Hello Math"); // OKstr2.assign(str1); // OKstr3.assign(str1,6,9); // OKstr4.assign(str1,5); // OKstr5.assign(3,'M'); // str5="MMM"

Page 43: 第十讲    类与对象(二)

43

更多操作str.append(str) 将字符串 str 追加到当前字符串后面str.append(str,k,n)

追加 str 从下标 k 开始的连续 n 个字符

str.append(str,n) 追加 str 的前 n 个字符str.append(n,c) 追加 n 个字符 c

string str1, str2;

str1="Hello "; str2="Math";str1.append(str2,1,3); str1.append(str2,3); str1.append(3,'Y');

Page 44: 第十讲    类与对象(二)

44

更多操作str.substr(str,k,n)

返回当前字符串从下标 k 开始的连续 n

个字符str.substr(str,n) 返回当前字符串从下标 k 开始的子串

str.compare(str) 与 str 比较 (大于为正 , 等于为 0, 小于为负 )

str.compare(k,n,str)

与 str 的字串 ( 从下标 k 开始的连续 n

个字符 ) 进行比较

str.insert(k,str) 在下标 k 位置插入字符串 str

str.insert(k,n,c) 在下标 k 位置连续插入 n 个字符 c

str.replace(k,n,str)

用 str 的内容替换从下标 k 开始的 n 个字符

Page 45: 第十讲    类与对象(二)

45

更多操作str.find(str) 返回 str 在当前字符串中首次出现的位置str.find(str,k)

同上 , 从下标 k 位置开始查找

str.find(c) 返回字符 c 在当前字符串中首次出现的位置str.find(c,k) 同上 , 从下标 k 位置开始查找

str.c_str() 将当前字符串转化为数组字符串str.data() 同上

更多成员函数参见 C++ Reference

Page 46: 第十讲    类与对象(二)

46

课后练习 课后练习(自己练习)

教材第十章:P. 326 10.2, 10.4

教材第十一章:P. 375 11.31P. 378 11.32, 11.33, 11.34P. 384 11.37, 11.38, 11.39

Page 47: 第十讲    类与对象(二)

47

上机作业

1) 设计一个名为 Rectangle2D 的类,表示平面坐标下的一个矩形,这个类包括:

注:若没有特别说明,所有数据成员都是 private ,函数成员都是 public

四个 double 型数据成员: x, y, width, height ,分别表示矩形中心坐标、宽和高

一个不带形参的构造函数,用于创建缺省矩形: (x,y)=(0,0), width=height=1 一个带形参的构造函数: Rectangle2D(double x, double y, double width, double height) 成员函数 getAera() ,返回矩形面积 成员函数 contains(double x, double y) ,当给定点 (x,y) 在矩形内时返回 true ,否则返回 false ,如下页图 a)

成员函数 contains(const Rectangle2D &r) ,当给定矩形在当前矩形内是返回 true ,否则返回 false ,如下页图 b)

成员函数 overlaps(const Rectangle2D &r) ,当给定矩形与当前矩形有部分重叠时返回 true ,否则返回 false ,如下页图 c)

实现这个类,并在主函数中测试这个类:创建矩形 r1(2,2,5.4,4.8), r2(4,5,10.6,3.3)

和 r3(3,5,2.2,5.5) ,输出 r1 的面积,以及 r1.contains(3,3), r1.contains(r2) 和r1.overlaps(r3) 的结果。程序取名为 hw10_01.cpp

Page 48: 第十讲    类与对象(二)

48

上机作业

2) 设计一个名为 MyDate 的类,表示日期,这个类包括:

a) b) c)

三个 int 型数据成员: year, month, day ,分别表示年、月、日 一个带一个形参的构造函数,用给出的自 1970年 1月 1日 0 时流逝的秒数创建

一个 MyDate 对象,如果没有给定时间,则缺省为当前时间: MyDate(unsigned long second=time(0)) 一个带三个形参的构造函数,用给定的年月日创建一个 MyDate 对象: MyDate(int year, int month, int day) 成员函数 showDay( ) ,在屏幕上输出对象中的年月日

实现这个类,并在主函数中测试这个类:创建表示当前时间的 MyDate 对象 d1

和 d2(3456201512) ,然后输出它们所对应的日期。程序取名为 hw10_02.cpp

Page 49: 第十讲    类与对象(二)

49

上机作业3) 设计 Employee 类,使用 string 对象,这个类包括:

四个 string 类数据成员: name, addr, city, zip ,分别表示姓名,街道地址, 省市,邮编

一个带形参的构造函数,用于初始化数据成员 成员函数 ChangeName ,修改姓名 成员函数 Display ,输出所有信息(即姓名,地址,省市和邮编) 数据成员是保护类型的,函数成员是公有类型的实现这个类,并在主函数中测试这个类:使用你自己的相关信息初始化数

据成员,并在屏幕上输出。程序取名为 hw10_03.cpp

class Employee{ public: Employee(const string &, const string &, cosnt string &, const string &); void ChangeName(string &); void Display(); protected: string name, addr, city, zip;};

Page 50: 第十讲    类与对象(二)

50

上机作业4) 字符易位破译,使用字符串类实现:编写函数,测试两个字符串是否字符异位相等,即两个字符串中包含的字母是相同的,但次序可以不同,如“ silent” 和“ listen” 是字符异位相等,但“ baac” 与“ abcc” 不是。 bool isAnagram(const string &str1, const string &str2); void sort(string &str) ;提示:先对字符串进行排序,然后再比较。程序取名 hw10_04.cpp 。