《后台开发核心技术与应用实践》第三章 读书笔记
第三章 常用STL的使用
-
STL是什么
STL使一个标准模板库,是一个高效C++程序库。
-
string
string类的底层是一个字符串指针。
普通构造函数
String::String(const char *str){
if(str == NULL){
m_data = new char[1];
*m_data = '\0';
}
else{
int length = strlen(str);
m_data = new char[length+1]; //+1用来存放'\0'
strcpy(m_data,str);
}
}
String的析构函数
String::~String(){
if(m_data){
delete[] m_data;
m_data = 0;
}
}
拷贝构造函数
String::String(const String &other)[
if(!other.m_data){
m_data = 0;
}
m_data = new char[strlen(other.m_data)+1];
strcpy(m_data,other.m_data);
]
赋值函数
String & String::operator=(const String &other){
if(this != other){
delete[] m_data;
if(!other.m_data){
m_data = 0;
}
else{
m_data = new char[strlen(other.m_data)+1];
strcpy(m_data,other.m_data);
}
return *this;
}
}
如果传入的参数内容与本身的内容一致,不需要赋值。如果传入的参数内容与本身内容不一致,需要先清空本身的内容。
字符串连接
String & String::operator +(const String &other){
String newString;
if(!other.m_data){
newString = *this;
}
else if(!m_data){
newString = other;
}
else{
newString.m_data = new char[strlen(m_data)+strlen(other.m_data)+1];
strcpy(newString.m_data,m_data);
strcat(newString.m_data,other.m_data);
}
return newString;
}
判断相等
bool String::operator == (const String &other){
if(strlen(m_data)!=strlen(other.m_data)){
return false;
}
else{
return strcmp(m_data,other.m_data)?false:true;
}
}
C++字符串转换成对应的C字符串:data(),c_str(),copy().data()以字符数组的形式返回字符串内容,并不添加‘\0’,c_str()返回一个以’\0’结尾的字节数组,copy()把字符串的内容复制或写入既有的c_string或字符数组内。
sting和int类型的转换
(1) int转string:用到snprintf()函数,函数原型是
int snprintf(char *str,size_t size,const char *format,可变参数)
(2) sting转int:用到strtol,strtoll,strtoul,strtoull函数,函数原型是
long int strtol(const char *nptr,char **endptr,int base);
long long int strtoll(const char *nptr,char **endptr,int base);
unsigned long int strtoul(const char *nptr,char **endptr,ing base);
unsigned long long int strtoull(const char *nptr,char **endptr,int base);
String的其它常用成员函数
3.vector
vector是线性容器,和动态数组类似。元素存储在以快连续的存储空间中。容器的大小一般会小于等于容器的容量,vector::size()返回容器的大小,vector::capacity()返回容量值。
vector的遍历有这几种方式
for(int i=0;i<a.size();++i);
for(iter=ivector.begin();iter!=ivector.end();iter++);
for_each;
在vector中存放结构体时,可以按照自己定义的排序方式排序。
可以在结构体外定义比较函数,然后再sort中调用比较函数cmp。
vector的查找可以使用find函数,这里注意find函数不是vector成员,所以应该加头文件#include
vector的删除,可以有erase或pop_back函数,erase可以删除指定元素或指定位置的元素,而pop_back只能去掉数组的最后一个数据。
vector的增加,可以有insert和push_back,insert是插入元素到某个位置中,push_back是在最后添加一个元素。
当进行元素添加时,如果原来的内存不够用,就会动态分配当前大小的1.5-2倍新内存,然后把元素赋值过去。一般来说,vector的访问速度和一般数组相比差不多,只有在重新分配时,性能才会下降。如果有大量数据需要进行push_back,应当使用reserve()函数提前设定容量大小。
使用“交换技巧”来修整vector过剩空间/内存
vector<int>(ivec).swap(ivec)
vector在不同情况下占用内存空间的大小情况:vector是按照容器现在容量的一倍进行增长,并不是在原有连续的内存空间后再进行简单的叠加,而是重新申请一块更大的新内存,并把现有容器中的元素逐个复制过去,同时销毁旧的内存。这时原有指向就内存空间的迭代器已经失效,所以当操作容器时,迭代器要及时更新。
4.map
map本质在于元素的值与某个特定的键相关联。特点是增加和删除结点对迭代器的影响很小。map的底层实现是红黑树。map内部所有的数据是有序的。
map的插入
map的插入有三种方式:用insert函数插入pair数据,用insert函数插入value_type数据和用数组方式插入数据。
第一种和第二种用insert函数插入数据,当map中有这个关键字时,insert操作是不能插入数据的,用数组则会覆盖以前该关键字对应的值。
map的遍历
map的遍历有三种方法:应用前向迭代器、应用反向迭代器和数组方式。
用数组访问vector时,下标是从0size-1,用数组访问map,下标是1size。
map的查找
用find函数来定位数据出现的位置,返回一个迭代器,当数据出现时,返回数据所在位置的迭代器,如果map中没有要查找的数据,返回迭代器等于end函数返回的迭代器。
map的删除
用erase方法可以删除map中的元素。在删除元素的时候要注意迭代器是否失效。
map的排序
map的排序默认是按照key从小到大排序,less是一个函数对象,实质是对operator()操作符的重载,与less相对的有greater。
map中的key是结构体,如果没有重载<操作,就会导致insert在编译时无法成功,因为map的底层实现是红黑树,在插入<key,value>时,会按照key的大小顺序进行存储,这也是作为key的类型必须能够进行<运算比较的原因。
因为sort只能对线性存储的元素进行排序,对于map这种底层是红黑树的非线性存储无法直接进行排序,可以把map中的元素放到vector中,然后再调用sort,这里可以对<重载,实现按value排序的功能。sort和map一样,也可以对指定元素进行比较,指定Compare,map是在定义时指定的,所以传参的时候直接传入函数对象的类名,sort算法是咋调用时指定的,需要传入一个对象。
map的原理
map内部是一颗红黑树,会对数据进行自动排序。红黑树是一种二叉查找树。增加了着色相关性质使红黑树相对平衡。为了使红黑树始终保持logn的高度,有以下性质:
- 根节点是黑的
- 每个叶子结点,也就是树尾端的NULL结点是黑色
- 不允许在一条路径上出现连续的两个红色结点
- 对任意结点而言,其到叶结点树尾端的每条路径都包含相同数目的黑结点
对红黑树进行插入和删除操作时,可能会破坏红黑树的性质,为了保持红黑树的性质,可以对结点进行颜色翻转和左旋右旋操作,来使红黑树保持原来的性质。
5.set
在STL中,vector封装了数组,list封装了链表,map和set封装了二叉树。
set是什么?
set中每个元素的值使唯一的,而且根据元素的值自动进行排序。STL中set,multiset,map,multimap底层都是红黑树。
map和set的插入删除效率为什么要比其它的序列容器高?
set和map不要做内存拷贝和内存移动。set容器所有元素都是以结点的方式来才能出,结点结构和链表差不多,指向父结点和子结点,插入的时候只需要更改结点的指针指向新的结点就可以了,删除的时候也只需要对指针进行操作,这里操作都是与指针相关,不需要移动内存。
为什么每次insert后,之前的iterator不会失效?
iterator使指向结点的指针,这里的内存没有变,对于vector,每一次删除和插入,指针有可能失效是因为保证内部数据的连续存放,iterator指向的内存在删除和插入过程可能被其它内存覆盖或者内存已经被释放了,而且可能因为内部空间不够,需要一块新的内存,然后把原来的数据元素复制过来,所以此时itertator失效了。
set的操作
set插入元素的三种操作:
- 插入value,返回pair配对对象,可以根据.second判断是否插入成功。
- 在pos位置之前插入value,返回新元素位置,但不一定能插入成功。
- 讲迭代区间[&first,&last)内所有的元素,插入到set容器。
set删除元素的操作:
- size_type erase(value) 移除set容器内元素为value的所有元素,返回移除的元素个数
- void erase(&pos) 移除pos位置上的元素,无返回值
- void erase(&first ,&last) 移除迭代区间[&first,&last)内的元素,无返回值
- void clear(),移除set容器内所有元素
set元素查找操作:
- count(value)返回set对象内元素值为value的元素个数
- iterator find(value) 返回value所在位置,找不到value返回end()
set的其它常用方法:
- begin(),返回set容器的第一个元素
- end(),返回set容器的最后一个元素
- clear(),删除set容器中的所有元素
- empty(),判断set容器是否为空
- max_size(),返回set容器可能包含的元素最大个数
- size(),返回当前set容器中的元素个数
- rbegin(),返回的值和end()相同
- rend(),返回的值和rbegin()相同