一、什么是封装

C++中有这样一句话,万事万物皆可为对象。

对象指的是某一个类的具体实例化。而类就是将某一类事物的属性和行为进行封装。将类封装起来,就能控制类的属性和行为。

总而言之,封装就是将复杂事物变得能让人轻易就能使用的方法,就像手机、汽车等,都是封装好的成品,只需要掌握操作的接口,就能轻松使用。

二、为什么要封装

一部手机是由各种精密的零件,组件和芯片等组成,由这些精美零件等制成的手机拥有了各种功能。手机就是类,它的属性就是零件,组件,芯片,行为是基于属性衍生出来的、所拥有的功能。对于封装的手机,我们即使不清楚他的底层实现,也可以轻易的上手使用。而封装好的手机,也是对本身的保护,不清楚底层实现原理的我们,看到的如果是各种线路板,既不能方便的使用手机,也无法保证手机的安全使用。

1.数据结构中的栈

在数据结构中有这样一种存储结构----栈

简单了解一下栈:后进先出,只允许在栈顶插入和删除数据

栈的属性,或者说栈是由哪些元素构成,如何用代码描述一个栈

typedef int DataType;
typedef struct Stack
{
	DataType* a;//用指针来存放栈中的元素
	int top;//记录栈中的元素个数
	int capacity;//记录栈空间的大小
}ST;

栈的行为,或者说栈如何使用,拥有什么功能(具体功能实现代码不做显示)

void STInit(ST* pst);
//栈的初始化,因为栈空间本身是由指针指向,指针需要初始化
void STDestory(ST* pst);
//栈的销毁,在实际开辟栈空间时,需要利用malloc函数在堆上开空间,有malloc,就
//必须有free,防止内存泄漏
void STPush(ST* pst,DataType x);
//入栈,在栈顶增添栈数据
void STPop(ST* pst);
//出栈,删除栈顶的数据
DataType STTop(ST* pst);
//返回栈顶元素
bool STEmpty(ST* pst);
//判断栈是否为空
int  STSize(ST* pst);
//返回栈的元素个数

2.关于内存泄漏

对某块分配的内存空间使用结束后,确没有释放。举例:

一个垃圾桶,相当于开辟的空间,你需要使用它,所以垃圾桶逐渐被垃圾填满,当向垃圾桶扔了几天垃圾后,你不想再用它了,于是你又买了一个垃圾箱,并且对旧的垃圾桶不做任何处理,任由其占据着一定的空间。

栈的实例化使用:

创建栈时,必须进行初始化,栈使用结束后,必须进行销毁。且是每次创建栈时所进行的必须操作。

C++之封装_字符串



3.封装的好处

1.对于某些动态开辟空间的类,无需每次都进行初始化和销毁的操作

2.封装是为了更方便,更好的使用

封装就是将一个成功研发的产品直接让你使用,这个产品的实现可能是无比复杂的,但是作为一个使用者和支配者,我们只需要掌握实现复杂功能的接口即可。例如玩手机和开汽车,手机和汽车的内部构造我们可能不清楚,但这并不妨碍我们熟练的使用他们。

3.保护类中的隐私数据。通过类成员访问限定符,可以限制类中成员的访问权限,以此提供保护。

三、关于string类

string类,也即字符串类,是一个封装好的类。它提供了各式各样的接口函数,只要掌握了这些接口函数,就可以轻松的使用string类,即使我们不清楚底层实现原理。

使用string需要包含的头文件:#include<string>(注意要展开命名空间)

string类的接口函数:

1.成员函数之构造函数

C++之封装_封装_02

分别是构造函数,析构函数,赋值运算符重载

构造函数的接口函数有很多,如下图

C++之封装_string类介绍使用_03

依次来看看这些构造函数的接口函数的具体使用

default(1)

C++之封装_string类介绍使用_04

通过调试后,s1的监视窗口如上图

其中描述s1的有size,capacity以及_Buf,分别表示的是字符串s1的字符个数,开辟空间的大小,以及存储的数据

我们可以看到,即使没有对字符串初始化,无参构造函数也会对字符串开辟空间,并放入\0

copy(2) string(const string& str);

函数分析:str接收字符串进行初始化,字符串末尾有‘\0’,如下图

C++之封装_string类介绍使用_05

函数解释:接收字符串对s2进行初始化

substring(3) string(Const string& str,size_t pos,size_t len=npos;

函数解释:

C++之封装_string类介绍使用_06

C++之封装_字符串_07

拷贝从字符串str下标问pos的位置开始拷贝len个字符,如果str太短(len的长度大于str的长度)或者没有给len传参,会一直拷贝到str的末尾

虽然缺省参数npos的定义中给的值是-1,但因为类型为size_t,实际值如下图

C++之封装_封装_08

函数使用:如下图

C++之封装_封装_09

C++之封装_string类介绍使用_10


在实际使用中发现,如果没有给len 传递参数并且使用常量字符串进行传参,该构造函数完成的是拷贝str的pos个字符,这是因为编译器识别成了另一个函数:

string (const char* s, size_t n);

该函数也是string类的构造函数,功能是拷贝s指向的n个字符

这是因为常量字符串的类型是const char*,相比const string& str,第一个参数与该函数更加匹配。如下图

C++之封装_迭代器_11

C++之封装_迭代器_12

如果使用string类的对象初始化,则不会出现错误调用的问题,如下图

C++之封装_封装_13


from c-str string(4)  string(const char* a);

函数解释:拷贝指针a指向的内容如下图

C++之封装_迭代器_14

from sequece(5) string (const char* a,size_t n);

函数解释:拷贝指针a指向的size个字符。如下图s5

C++之封装_封装_15

fill(6)  string (size_t n,char c);

函数解释:拷贝n个字符c。如下图

C++之封装_string类介绍使用_16

range (7)

template<class InputIterator>

string<InputIterator first,InputIterator last>;

函数解释:拷贝迭代器first到last所指向范围的内容

函数使用:如下图

C++之封装_string类介绍使用_17

2.关于迭代器

2.1什么是迭代器

简单来说,我们可以将迭代器当作是指针使用。但实际上,迭代器的类型不同,使用方法也不同。

2.迭代器的分类

输入迭代器:如下图

C++之封装_封装_18

C++之封装_string类介绍使用_19

输出迭代器:

C++之封装_迭代器_20

C++之封装_封装_21

前向迭代器:如下图

C++之封装_封装_22

C++之封装_string类介绍使用_23


双向迭代器:如下图

C++之封装_字符串_24

C++之封装_字符串_25

支持++和--操作所有双向迭代器也是有效的前向迭代器和输入迭代器。


随机迭代器:如下图

C++之封装_字符串_26

C++之封装_封装_27

C++之封装_string类介绍使用_28

C++之封装_迭代器_29

2.3常用的迭代器函数

来看看sring类中迭代器的几种玩法

C++之封装_string类介绍使用_30

C++之封装_string类介绍使用_31

函数解释:begin是string类的公共成员函数之一,它的功能是返回指向的字符串的第一个字符位置的迭代器,如下图

C++之封装_封装_32

C++之封装_迭代器_33

函数解释:返回指向字符串string最后一个字后位置的迭代器iterator

end指向的是字符串的末尾,不包含在string空间内。如下图,先--,才能访问最后一个元素

C++之封装_string类介绍使用_34

关于rbegin:指向的位置是反方向的第一个字符,也就是最后一个字符

C++之封装_字符串_35

关于rend:指向的位置是反向结束的位置,也就是第一个字符前

C++之封装_字符串_36

如下图

C++之封装_string类介绍使用_37


2.4const修饰的隐含的this指针

const对象只能使用const_iterator类型的迭代器,普通对象既可以使用iterator,也可以使用const_iterator类型的迭代器,但在实际调用时,没有const修饰的迭代器函数调用的优先级要更高,会优先调用

C++之封装_封装_38

2.5权限放大缩小问题

规定:权限只能缩小或者平移,而不能放大。

如果const对象的权限是只读,不能写

普通对象的权限是可读可写,

cosnt参数接收const对象,属于权限的平移,被允许。接收普通对象,属于权限的缩小,被允许。

普通参数接收普通对象,属于权限的平移,被允许。接收const对象,属于权限的放大(将只读权限更改为可读可写),不被允许。

3.成员函数之析构函数

C++之封装_封装_39

函数解释:程序结束是销毁string对象指向的空间,无需手动调用

4.成员函数之赋值运算符重载

C++之封装_迭代器_40

赋值运算符是两个已经存在的string对象进行赋值操作

string(1) string& operator=(const string& str);

函数解释:赋值,用新的字符串取代旧的字符串

C++之封装_字符串_41

C++之封装_封装_42

string(2) string& operator=(const char* s);

函数解释:赋值,用指针指向的新的字符串取代旧的字符串

string(3) string& operator=(char c);

函数解释:赋值,用新的字符取代旧的字符串

C++之封装_字符串_43

C++之封装_封装_44

5.非成员函数的重载


C++之封装_封装_45

5.1operator+

C++之封装_字符串_46

函数解释:并不会改变原本字符串,可以直接输入lhs(左字符串)+rhs(右字符串)的结果。如下图

C++之封装_string类介绍使用_47

5.2operator>>

C++之封装_string类介绍使用_48

函数解释:输入运算符重载。完成写操作(在黑框框中写入)

5.3operator<<

C++之封装_迭代器_49

函数解释:输出运算符重载。完成读操作(在黑框框中显示)

6.访问字符串数据的接口函数

C++之封装_迭代器_50

6.1对[]的运算符重载

对[]进行运算符重载后,使得字符串类也支持下标随机访问

C++之封装_封装_51

函数解释:返回字符串pos下标位置的字符。如下图

C++之封装_迭代器_52

C++之封装_string类介绍使用_53

返回字符串pos下标位置的字符,pos的大小要小于字符串的长度(否则会抛异常)

C++之封装_string类介绍使用_54

7.修改字符串的常用函数

C++之封装_字符串_55

7.1operator+=

C++之封装_封装_56

函数解释:在当前字符串的结尾追加字符串

用法:如下图

C++之封装_迭代器_57

7.2append

C++之封装_string类介绍使用_58

函数解释:为字符串追加字符串。相比+=有了更多的玩法。

string(1):直接追加字符串。

string(2):从字符串str下标为subpos的位置追加sublen个字符串,如果没有传参数给sublen或者str太短,则追加str的全部字符。如下图

C++之封装_字符串_59

C++之封装_字符串_60

7.3insert

C++之封装_字符串_61

函数解释:在字符串的下标为pos的位置前插入额外的字符。插入位置后原本位置的数据会在插入的数据后。

用法:

string(1):如下图

C++之封装_封装_62

single character(6)

C++之封装_封装_63

7.4 erase

C++之封装_字符串_64

函数解释:

string(1):从字符串下标为pos的位置删除长度为len个字符,pos默认为下标0,len默认到字符串结尾。

string(2):删除迭代器指向位置的字符。

string(3):删除从first位置到last位置的字符。(只删first下标的字符,不删除last下标的字符)。如下图

C++之封装_封装_65

7.5push_back

C++之封装_封装_66

函数解释:尾插,字符串尾部插入一个字符。如下图

C++之封装_迭代器_67