第十一讲 类与对象 (III) 面向对象提高
目录页 华东师范大学数学科学学院 School of Mathematical Sciences, ECNU Contents 1 2 常对象与常成员 对象数组与对象指针 3 向量类 :vector 4 字符串类 :string
1 常对象与常成员 为什么需要常对象 常对象的声明 常数据成员 常函数成员 常引用 关键字 const 3
为什么常对象 为什么常对象 : 将对象声明成常对象, 可以有效地保护数据 常对象的声明 const 类名对象名 ; // OK 类名 const 对象名 ; // OK 常对象必须进行初始化 常对象 : 数据成员均为常量 NOTE 不能通过常对象调用普通成员函数! 常对象只能作为常成员函数的目的对象! 4
常成员 常成员函数类型说明符函数名 ( 形参 ) const; 常对象只能调用常成员函数 常成员函数可以被普通对象调用 无论对象是否为常对象, 常成员函数的目的对象都将被视为常对象 常数据成员 将数据成员声明为常量 必须初始化 ( 采用参数初始化表, 不能在构造函数内赋值 ) // 假定数据成员 a 和 b 是常量,c 是普通变量 Myclass::Myclass(int x, int y, int z): a(x), b(y) { c=z; } Example 5
class 常成员 Myclass {...... void display() const; void show();...... const int a; int b; }; Myclass::Myclass(int x, int y): a(x) { b = y; }...... int main () { Myclass obj1(2,3); const Myclass obj2(4,5); Myclass const obj3(6,7); obj1.display(); // OK obj2.display(); // OK obj3.display(); // OK obj1.show(); // OK obj2.show(); // ERROR obj3.show(); // ERROR } ex11_class_const.cpp 6
常引用 常引用 const 类名 & 引用名 ; 常引用可以绑定到常对象, 也可以绑定到普通对象 不能通过常引用来修改常引用所绑定的对象 普通引用不能绑定到常对象 ex11_ref_const.cpp 在 C++ 编程中, 经常用常指针和常引用作函数参数, 这样既可以节省存储 量和运算时间, 又能保证数据的安全 7
2 对象数组与对象指针 对象数组的声明与初始化 指向对象的指针 this 指针 特殊符号 :-> 指向成员的指针 持久内存分配 :new delete 8
对象数组的声明与引用 类名数组名 [n]; // 声明 数组名 [k]. 成员名 ; // 使用 对象数组的初始化 : 对每个元素都调用构造函数..... Point() { x=0; y=0} Point(int a, int b) { x=a; y=b; }...... Example int main() { Point A[2]={Point(), Point(2,3);...... } 9
对象指针 对象指针 : 指向对象的指针, 存放对象的地址 对象指针的声明类名 * 对象指针名 ; Point a(1,2); Point * pa = &a; Example 使用对象指针访问对象成员 : -> 对象指针名 -> 成员名 // 特有的方式, 推荐使用 (* 对象指针名 ). 成员名 // 普通方式 指向常对象的指针 :const 类名 * 对象指针名 ; 10
this 指针 this 指针 : 隐含在非静态成员函数中的特殊指针, 指向目的对象 this 指针是常指针 若局部作用域中声明了与类成员同名的标识符 ( 如变量名 ), 则可以通过 this 指针访问目的对象的成员 当通过一个对象调用成员函数 ( 非静态成员函数 ) 时, 系统会把 该对象的起始地址赋给成员函数的 this 指针 NOTE 非静态成员函数有 this 指针, 而静态成员函数没有 this 指针 11
例 :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; } ex11_this.cpp }; private: // 私有数据 int x, y; 12
直接指向成员的指针 指向成员指针 : 直接指向类的成员的指针 指向非静态成员的指针类型说明符类名 ::* 指针名 // 指向数据成员类型说明符 ( 类名 ::* 指针名 )( 参数 ) // 指向函数成员 指向静态成员的指针 对类的静态成员的访问不依赖于对象, 因此可以通过普通 指针来访问静态成员 13
指向非静态成员 指向数据成员指针的赋值与引用 指针名 = & 类名 :: 数据成员名 对象名.* 指针名对象指针名 ->* 指针名 指向函数成员指针的赋值与引用 指针名 = & 类名 :: 函数成员名 ( 对象名.* 指针名 )( 参数 ) ( 对象指针名 ->* 指针名 )( 参数 ) 14
持久动态内存分配 动态对象的创建 类名 * 指针名 =new 类名 () 类名 * 指针名 =new 类名 ( 参数列表 ) // 不带参数 // 带参数 动态对象的释放 delete 指针名 15
3 向量类 vector 头文件 vector 向量对象 向量操作 16
向量类 C++ 提供了向量类, 使用向量与使用数组一样, 但向量的长度 可以根据需要自动增减, 比普通数组更灵活 向量的声明 vector< 类型说明符 > 向量名 ; 加头文件 vector #include <vector>...... vector<int> x(8); // 创建一个长度为 8 的整型向量 vector<double> y(6); // 创建一个长度为 6 的 double 型向量 可创建不同类型的向量, 注意 :x 是对象, 不是数组名 向量是对象, 所以创建时会初始化 ( 调用相应的构造函数 ) 17
向量类的构造函数 ( 部分 ) vector<type>(); // 缺省构造函数, 创建一个空向量 vector<type>(int n); // 创建长度为 n 的向量 vector<type>(int n, Type x); // 创建长度为 n 的向量, 并用 x 初始化所有分量 这里的 Type 可以是基本数据类型, 如 int,float 等, 也可以是类 vector<int> x(100); // 创建长度为 100 的整型向量 Example vector<float> y(10, 2.1); // 创建长度为 10 的单精度型向量, 初值都是 2.1 vector<double> z; // 创建一个双精度型空向量, 即长度为 0 18
向量基本操作 v[k] 第 k 个分量, 下标从 0 开始 v1 = v2 赋值 ( 复制 ) v1 == v2 个数和值都相等时返回真 v1!= v2 不相等时返回真 <, <=, >, >= 按字典顺序进行比较 vector<int> x(10); for(int k=0; k<10; k++) x[i] = i; OK vector<int> x; for(int k=0; k<10; k++) x[i] = i; ERROR 19
常用成员函数 at(int k) 返回下标为 k 的分量 size() 返回向量的长度 clear() 清空向量中的数据 empty() 判断向量是否为空 ( 长度为 0) front() 返回第一个分量的值 back() 返回最后一个分量的值 push_back( 数据 ) 在向量末尾插入数据 pop_back() 删除最后一个分量 swap(vector) 交换向量的值 向量是对象, 向量名不是地址! ex10_vector.cpp 关于 vector 的更多介绍参见相关资料 20
向量举例 例 : 找出给定区间内的所有素数 void find_prime(int a, int b, vector<int> & x) { for (int k=a; k<=b; k++) { if (is_prime(k)) x.push_back(k); } } ex10_vector_prime.cpp 21
4 字符串类 string 字符串对象与字符数组 string 类的构造函数 字符串各种操作 关键字 string 22
字符串类 在 C++ 中, 字符串可以通过字符数组实现, 也可以通过 string 类实现, 但后者更方便 #include <string> // 注意不是 cstring...... string str; // 定义一个空字符串对象 string str1="math.", str2("ecnu"); // 可以初始化 string str3=str1+str2; // str3="math.ecnu" string str4(5,'c'); // 连续 5 个字符 c 加头文件 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 次 24
输入输出 string 对象的输入输出 cin >> str; cout << str; 输入 getline getline(cin,str); // 以换行符作为输入结束符 getline(cin,str,'c'); // 将字符 'c' 作为输入结束符 25
基本操作 操作符示例功能 + = += ==!= < <= > >= str[i] str1 + str2 str1 = str2 str1 += str2 str1 == str2 str1!= str2 str1 < str2 str1 <= str2 str1 > str2 str1 >= str2 连接两个字符串用 str2 更新 str1, 即复制 str1 = str1 + str2 比较字符串大小比较字符串大小比较字符串大小比较字符串大小比较字符串大小比较字符串大小访问下标为 i 的字符, 数组方式 以上操作通过操作符重载实现 比较大小按字典顺序, 从前往后逐个进行比较 26
基本操作举例 string str1="hello"; string str2(3,'a'); string str3 = str1 + " Math"; Example 两个 string 对象可以相加 string 对象和数组字符串也可以相加 但两个数组字符串不能直接相加 string str4="hello" + " Math"; // ERROR ex10_string.cpp string str4="hello" + str2 + " Math"; // OK 27
基本操作举例 string 对象可以直接赋值 但数组字符串不能! string str1; char str2[10]; Example str1="hello"; // OK str2="hello"; // ERROR 28
更多操作 str[k] 返回第 k 个字符 str.at(k) 返回第 k 个字符 ( 会自动检测是否越界 ) str.length() 字符串对象的长度 ( 字符个数 ) str.size() str.capacity() str.clear() str.erase(k,n) str.empty() str.push_back(...) str.pop_back() 同上返回为当前字符串分配的存储空间清除字符串中所有内容从下标 k 开始, 连续清除 n 个字符判断 str 是否为空在最后面插入一个字符删除最后一个字符 29
更多操作 str.assign(str1) str.assign(cstr) str.assign(str1,k,n) str.assign(str1,n) str.assign(n,c) 用字符串对象赋值用数组字符串赋值用 str1 从下标 k 开始的连续 n 个字符赋值用 str1 前 n 个字符赋值用 n 个字符 c 赋值 string str1, str2, str3, str4, str5; str1.assign("hello Math"); str2.assign(str1); str3.assign(str1,6,9); str4.assign(str1,5); str5.assign(3,'m'); // OK // OK // OK // OK // str5="mmm" 30
更多操作 str.append(str1) str.append(str1,k,n) str.append(cstr,n) str.append(n,c) 将字符串 str1 追加到当前字符串后面追加 str1 从下标 k 开始的连续 n 个字符追加数组字符串 cstr 的前 n 个字符追加 n 个字符 c string str1, str2; char cstr[]="okay"; str1="hello "; str2="math"; str1.append(str2,1,3); str1.append(cstr,3); str1.append(3,'y'); // Hello ath // Hello athoka // Hello athokayyy 31
更多操作 str.substr(k,n) str.substr(k) 返回当前字符串从下标 k 开始的连续 n 个字符 返回当前字符串从下标 k 开始的子串 str.compare(str1) 与 str1 比较 ( 大于为正, 等于为 0, 小于为负 ) str.compare(k,n,str1) 与 str1 的字串 ( 从下标 k 开始的连续 n 个字符 ) 进行比较 str.insert(k,str1) str.insert(k,n,c) str.replace(k,n,str1) 在下标 k 位置插入字符串 str1 在下标 k 位置连续插入 n 个字符 c 用 str1 的内容替换从下标 k 开始的 n 个字符 32
更多操作 str.find(str1) str.find(str1,k) str.find(c) str.find(c,k) 返回 str1 在当前字符串中首次出现的位置同上, 从下标 k 位置开始查找返回字符 c 在当前字符串中首次出现的位置同上, 从下标 k 位置开始查找 str.c_str() str.data() 将当前字符串转化为数组字符串 同上 更多成员函数可参见 C++ Reference 33
字符串举例 例 : 二进制转化为十进制 long bin2dec(const string & str) { long num=0; for (int i=0; i<str.length(); i++) num = num*2 + str[i]-'0 ; return num; } ex11_string_bin2dec.cpp int main() { string str; cout << " 请输入二进制数 :"; getline(cin,str); cout << " 对应的十进制数为 :" << bin2dec(str) << endl; return 0; } 34
上机作业 1 设计一个名为 Rectangle2D 的类, 表示平面坐标下的一个矩形, 这个类包括 : 四个 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) 的结果 ( 程序取名为 hw11_01.cpp) a) b) c) 35
上机作业 2 字符易位破译, 使用 string 类实现 : 编写函数, 测试两个字符串是否字符异位相等, 即两个字符串中包含的字母是相同的, 但次序可以不同, 如 silent 和 listen 是字符异位相等, 但 baac 与 abcc 不是 bool isanagram(const string & str1, const string & str2); void sort(string & str); 提示 : 先对字符串进行排序, 然后再比较 ( 程序取名 hw11_02.cpp) 36
上机作业 3 设计 Employee 类, 使用 string 对象, 这个类包括 : 四个 string 类数据成员 :name, addr, city, zip, 分别表示姓名, 街道地址, 省市, 邮编 一个带四个形参的构造函数, 用于初始化数据成员 成员函数 ChangeName, 修改姓名 成员函数 Display, 输出所有信息 ( 即姓名, 地址, 省市和邮编 ) 数据成员是保护类型的, 函数成员是公有类型的 实现这个类, 在主函数中测试这个类 : 使用你自己的相关信息初始化数据, 并在屏幕上输出 ( 程序取名为 hw11_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; }; 4 ( 可选题 ) 用向量类实现矩阵的求和与乘积计算 ( 程序取名为 hw11_04.cpp) 37