C++:标准库类型(string、vector、list、bitset)

C++中两种最重要的标准库类型是 string 和 vector。首先介绍一下迭代器。

迭代器

现代 C++ 程序更倾向于使用迭代器而不是下标操作访问容器元素,即使对支持下标操作的 vector 类型也是这样。

每种容器类型都定义了自己的迭代器类型,如 vector:

vector<int>::iterator iter;

该语句定义了一个名为 iter 的变量,它的数据类型是 vector 定义的 iterator 类型。

一些典型的迭代器操作

begin 函数:

把 iter 初始化为由名为 vector 操作返回的值。假设 vector 不空,初始化后,iter 即指该元素为 ivec[0]。

end函数:

由 end 操作返回的迭代器指向 vector 的“末端元素的下一个”,表明它指向了一个不存在的元素。如果 vector 为空,begin 返回的迭代器与 end 返回的迭代器相同。

解引用操作符(*):

解引用操作符返回迭代器当前所指向的元素。

自增操作符(++):

向前移动迭代器指向容器中下一个元素。


由于 end 操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作。

比较:

用 == 或 != 操作符来比较两个迭代器,如果两个迭代器对象指向同一个元素,则它们相等,否则就不相等。

迭代器的使用

    int i = 1;  
    for(vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); iter++)  
    {  
        *iter = i++;  
    }  

const_iterator

每种容器类型还定义了一种名为 const_iterator 的类型,当我们对普通 iterator 类型解引用时,得到对某个元素的非 const。而如果我们对 const_iterator 类型解引用时,则可以得到一个指向 const 对象的引用,如同任何常量一样,该对象只读不写

使用迭代器的二分查找

STL中关于二分查找的函数有三个lower_bound 、upper_bound 、binary_search 。这三个函数都运用于有序区间(当然这也是运用二分查找的前提),下面记录一下这两个函数。

ForwardIter lower_bound(ForwardIter first, ForwardIter last,const _Tp& val)算法返回一个非递减序列[first, last)中的第一个大于等于值val的位置。

ForwardIter upper_bound(ForwardIter first, ForwardIter last, const _Tp& val)算法返回一个非递减序列[first, last)中的第一个大于值val的位置。

这里写图片描述

遍历元素的新方法:for(auto count : counts)

这是C++11中的语法,即:Range-based for loop。其中counts应满足:begin(counts), end(counts)是合法的。
它等价于

for(some_iterator p = begin(counts); p != end(counts); ++p)
//同时
some_type count = *p。

string 类型

与其他的标准库类型一样,用户程序要使用 string 类型对象,必须包含相关头文件。

#include <string>

string的初始化

string s1;//用默认构造函数创建s1,s1为空串
string s2=s1;//将 s2 初始化为 s1 的一个副本
string s3="value";//将 s3 初始化为一个字符串字面值副本
string s4(n,'c');//将 s4 初始化为字符'c'的n个副本

string的元素读取可以使用下标。

string的读取

cin>>s

• 读取并忽略开头所有的空白字符(如空格,换行符,制表符)。
• 读取字符直至再次遇到空白字符,读取终止。
如:输入“hello world”,那么会输出“hello”而没有空格。

getline(cin,s)

此时s读取的将是一行字符串,且不忽略空格,直到换行符出现。

用stringstream类实现字符串分割

假设s是一个以空格为分隔符的string变量,如何获得被分割的子string呢?可以使用stringstream。
C++引入了ostringstream、istringstream、stringstream这三个类,要使用它们创建对象就必须包含<sstream>这个头文件。

  • istringstream类用于执行串流的输入操作。
  • ostringstream类用于执行串流的输出操作。
  • strstream类同时可以支持串流的输入输出操作。

istringstream的构造函数原形如下:

istringstream::istringstream(string str);

它的作用是从string对象str中读取字符。

#include<iostream>  
#include<sstream>
#include<string>  
using namespace std;  
int main() {  
    string str="i am a boy";  
    istringstream is(str);  
    string s;  
    while(is>>s) {  
        cout<<s<<endl;  
    }  
} 

输出是:

i
am
a
boy

以上就实现了按空格分割字符串的功能。同样的功能也可以由stringstream实现。stringstream的str( s)方法將字符串s传入stringstream中,然后可以像使用cin那样使用stringstream。

注意到,一般还会调用ss.clear()方法清空目前的标志位。

    string str="i an a boy";
	stringstream ss;
	string as;
	ss.clear();
	ss.str(str);
    while(ss>>as) {  
        cout<<as<<endl; 
    }

如果不知道具体被分割单元的个数,还可以使用ss.fail()函数,该函数当ss流没有数据时返回true,可以据此停止读取string。在这里,ss起到和cin>>类似的作用。

	getline(cin, s);//从cin中读取了一个以回车结束的string
    ss.clear();
    ss.str(s);//将s转化成ss流
    sum=0;
    while (!ss.fail()) {
        ss >> a;
        sum+=a;
    }
    cout << sum << endl;

利用istringstream实现更通用的字符串分割

string本身没有提供切割的方法,但可以使用istringstream和getline方法实现分割。getline方法的第三个形参是分隔符。


PS:
这里getline的分隔符只能是字符,而不能是字符串。
对比Java,Java中的split方法是把字符串作为分隔符,所以相对来说更加方便。


如下:

    string str="i@am@a@boy";  

	istringstream is(str);
	vector<string> v;
	string a;

    while(getline(is,a,'@'))
		v.push_back(a); 
	for(int i=0;i<v.size();i++)
		cout<<v[i]<<endl;

输出为:

i
am
a
boy

常用的 string 方法

注:string::npos的类型是string::size_type,所以,一旦需要把一个索引与npos相比,这个索引值必须是string::size类型的。大多情况下,我们可以直接把函数和npos进行比较,如:
if(s.find(“jia”)== string::npos)

//string的大小
s.empty();//判断是否为空
s.size();
 

//在尾部添加字符
str1 += str2;
str.append(n, char); // 添加n个字符,也可以直接添加字符串
str.push_back(); // 字符


//赋值
s.assign(str);
s.assign(str,1,3);//如果str是”iamangel” 就是把”ama”赋给字符串
s.assign(str,2,string::npos);//把字符串str从索引值2开始到结尾赋给s
s.assign(“gaint”); 
s.assign(“nico”,5);//把'n' ‘I' ‘c' ‘o' ‘/0'赋给字符串
s.assign(5,'x');//把五个x赋给字符串


 //插入
//在字符串指定位置前面插入指定字符串  
str1.insert(14,str2);  
//在字符串指定位置前面插入指定字符串的子串(从指定索引开始的指定个数的字符) 
str1.insert(14,str2,2,9);  
//插入指定字符串的前n个字符  
str1.insert(14,"test hello",5);  
//插入n个相同字符到字符串中  
str1.insert(14,6,'*');  
          

//替换
直接坐标操作:str1[pos] = new_char;

//替换指定索引开始的指定长度的子串  
str1.replace(3,3,"may");  
//用给定字符串的指定子串来进行替换  
        //如下,实际上使用的是could来进行替换  
str1.replace(3,3,"can could",4,5);  
//使用给定字符串的前n个字符来进行替换
		//用can来进行替换
str1.replace(3,5,"can could",3);  
//使用指定个数的重复字符来进行替换  
str1.replace(3,3,5,'*');  
          

//交换
str1.swap(str2);//交换当前str1与str2的值


//删除
//删除从pos开始的n个字符,比如erase(0,1)就是删除第一个字符
str.erase(pos,n);
//删除iterator迭代器指向的一个字符
str.erase (iterator);
//删除从迭代器first到last之间的字符
str.erase (str.begin()+5, str.end()-7);

str.pop_back() // 删除最后一个字符
str.clear() //删除全部字符 


//查找
str.find(const string& str, size_t pos = 0);//返回符合搜索条件的字符区间内的第一个字符的索引,没找到目标就返回npos


//返回子串
s.substr();//返回s的全部内容
s.substr(11);//返回从索引11往后的子串
s.substr(5,6);//返回从索引5开始6个字符


//将string转换成整型数
//返回值:1. 成功转换显示一个Int类型的值. 2. 不可转换的字符串返回0.  3.如果转换后缓冲区溢出,返回 INT_MAX or INT_MIN
int a=atoi(s.c_str());
//或者:(利用了stringstream)
string s = "17";
int i;
stringstream ss;
ss<<s;
ss>>i;

//将string转换成float
//返回值:1. 转换成功返回doublel类型的值 2.不能转换,返回0.0。 3.越界,返回HUGE_VAL
double a=atof(s.c_str());

//整型转换成string
std::to_string(),参数可以是int, long,unsigned int,unsigned log,float,double等,优先使用。

(利用了stringstream)
int aa = 30;
stringstream ss;
string s2;
ss<<aa; 
ss>>s2;

//string转换成字符数组
char *p=str.c_str();//返回C的字符数组的指针


//比较
> < >= <= != //都被重载,用于string的比较

常用的字符(char)处理函数

isalnum(c);//如果 c 是字母或数字,则为 True。
isalpha(c);//如果 c 是字母,则为 true。
isdigit(c);//如果 c 是数字,则为 true。
isxdigit(c);//如果是 c 十六进制数,则为 true。


islower(c);//如果 c 是小写字母,则为 true。
isupper(c);//如果 c 是大写字母,则 true。
tolower(c);//如果 c 大写字母,返回其小写字母形式,否则直接返回 c。
toupper(c);//如果 c 是小写字母,则返回其大写字母形式,否则直接返回 c。

iscntrl(c);//如果 c 是控制字符,则为 true
isgraph(c);//如果 c 不是空格,但可打印,则为 true。
isprint(c);//如果 c 是可打印的字符,则为 true。
ispunct(c);//如果 c 是标点符号,则 true。
isspace(c);//如果 c 是空白字符,则为 true。

vector类型(容器向量)

vector的重要特性就是可以动态增长

使用 vector 之前,必须包含相应的头文件。

#include <vector>

vector的声明

vector 是一个类模板,声明从类模板产生的某种类型的对象,需要提供附加信息。以 vector 为例,必须说明 vector 保存何种对象的类型,通过将类型放在类模板名称后面的尖括号中来指定类型:

vector<int> ivec; // ivec holds objects of type int
vector<Sales_item> Sales_vec; // holds Sales_items

二维vector的声明如下:

vector<vector<int> > ary(row_num, vector<int>(col_num, 0));

注意两个右尖括号之间有一个空格。

vector 的初始化

vector<T> v(n,i);//v包含n 个值为 i 的元素
vector<T> v(v1);//v是v1 的一个副本
vector<T> v(n);//v包含n 个值初始化的元素
//数组初始化vector
 int iarray[]={1,2,3,4,5,6,7,8,9,0};
 size_t count=sizeof(iarray)/sizeof(int);
 //int数组初始化 ivec3 
 vector<int> ivec3(iarray,iarray+count);

vector 的常用操作函数

vector 标准库提供了许多类似于 string 对象的操作,表 3.5 列出了几种最重要的 vector 操作。

v.empty()如果 v 为空,则返回 true,否则返回 false。
v.size()返回 v 中元素的个数。
v.resize(n)重新设置v的大小为n
v.push_back(t)在 v 的末尾增加一个值为 t 的元素。
v.pop_back()删除v的尾端的元素
v.front()返回当前vector容器中起始元素的引用。
v.back()返回当前vector容器中末尾元素的引用。
v.insert(a,t)在迭代器a的前面增加一个值为t的元素。
v.erase(iter)删除指定位置的元素
v.erase(iterator first, iterator last)删除指定范围内的元素
v.clear()清空vector里的元素
v[n]返回 v 中位置为 n 的元素。
v1 = v2把 v1 的元素替换为 v2 中元素的副本。
v1 == v2如果 v1 与 v2 相等,则返回 true。
!=, <, <=,>,和>=保持这些操作符惯有的含义。

vector的添加

必须是已存在的元素才能用下标操作符进行索引,不能直接通过下标操作添加元素。
如果要添加新的元素,要用push_back函数:

ivec.push_back(ix);

或insert函数:
insert函数能在指定位置loc前插入值为val的元素,并返回指向这个元素的迭代器,如:

    v.insert(v.begin(),8);//在最前面插入新元素。  
    v.insert(v.begin()+2,1);//在迭代器中下标为2的元素前插入新元素  
    v.insert(v.end(),3);//在向量末尾追加新元素。

insert也能在指定位置loc前插入num个值为val的元素

vector <char>::iterator theIterator = alphaVector.begin(); 
alphaVector.insert( theIterator, 4, 'C' ); 
//在vector的开头添加了4个"C"

vector的删除

vector中删除指定元素使用了std::vector::erase()方法。
函数原型:

iterator erase (iterator position);  //删除指定元素
iterator erase (iterator first, iterator last);  //删除指定范围内的元素
v.erase(v.begin());//删除第一个元素
v.erase(v.end()-1);//删除最后一个元素,注意是v.end()-1
//当然删除最后一个元素也可以直接用v.pop_back()

注意,每一次删除之后,后边的元素都会向前移动。所以删除后,当前迭代器实际已经指向下一个元素,若再+1,实际上是指向了下一个元素的下一个元素。
因此,如果想删除vector中所有值为1的元素,应该这样写:

vector<int>::iterator itr = v.begin();
   while (itr!=v.end())
   {
	    if (*v==1)
	        itr=v.erase(itr);
		else
		    itr++;
   }

vector的查找

vector的查找需要用到迭代器,以及find函数
注意这里的查找范围[ array.begin(),array.end() )是一个左闭右开的。

 vector<int>::iterator s=find(array.begin(),array.end(),50);//第一个参数是array的起始地址,第二个参数是array的结束地址,第三个参数是需要查找的值
 
    if( s !=array.end())//找到
	    cout<<*s<<endl;
    else 
	    cout<<"not find!"<<endl;

list 类型

使用list前要包含相应的h文件:

#include <list>

list是非连续存储结构,具有双链表结构,每个元素维护一对前向和后向指针,因此支持前向/后向遍历。支持高效的随机插入/删除操作,但随机访问效率低下,且由于需要额外维护指针,开销也比较大。每一个结点都包括一个信息块Info、一个前驱指针Pre、一个后驱指针Post。可以不分配必须的内存大小方便的进行添加和删除操作。使用的是非连续的内存空间进行存储。


如果你需要大量的插入和删除,而不关心随机存取,则应使用list。

list的双向迭代器

list的迭代器是双向迭代器,只能++和–,而不能偏移(如:iter+5)。

list的常用方法

//链表的初始化是使用指针的,所以使用的是数组的地址
int num[10]={0,1,2,3,4,5,6,7,8,9};
list<int> link(&num[0],&num[9]+1);

//返回list中的元素个数
size() 
//如果list是空的则返回true 
empty() 
//删除所有元素
clear() 
//改变list的大小
resize() 

//把list的元素倒转
reverse() 

//合并两个list 
merge()

//返回迭代器
begin() //返回指向第一个元素的迭代器
end() //返回末尾的迭代器
rbegin() //返回指向第一个元素的逆向迭代器
rend() //指向list末尾的逆向迭代器

//返回元素
front() //返回第一个元素
back() //返回最后一个元素

//将元素e插入到迭代器iter指向的位置,之后,iter自动后移一位,指向原来那个元素
insert(iter,e) 
//在list的末尾添加一个元素
push_back() 
//在list的头部添加一个元素
push_front() 

//删除iter指向位置的节点
erase(iter) 
//删除list中所有值为e的节点
remove(e) 
//去除list中相邻的重复元素
unique() 
//删除最后一个元素
pop_back() 
//删除第一个元素
pop_front() 

//list有自带的sort函数,不需要调用algorithm里的sort函数
sort()

bitset 类型

同样的,先包含标准库:

#include <bitset>

初始化bitset 对象的方法

bitset bb 有 n 位,每位都 0
bitset b(u)b 是 unsigned long 型 u 的一个副本
bitset b(s)b 是 string 对象 s 中含有的位串的副本
bitset b(s, pos, n)b 是 s 中从位置 pos 开始的&nbps;n 个位的副本

	bitset<16> bit1("1101");//bit1=0000 0000 0000 1101
	bitset<16> bit1(1101);//bit1是十进制数1101的二进制表示
	bitset<16> bit1(0x1101);//bit1=0001 0001 0000 0001

###注:
string 对象和 bitsets 对象之间是反向转化的:string 对象的最右边字符 (即下标最大的那个字符)用来初始化 bitset 对象的低阶位(即下标为 0 的位)。

一些常用操作

b.any()b 中是否存在置为 1 的二进制位?
b.none()b 中不存在置为 1 的二进制位吗?
b.count()b 中置为 1 的二进制位的个数
b.size()b 中二进制位的个数
b[pos]访问 b 中在 pos 处二进制位
b.test(pos)b 中在 pos 处的二进制位置为 1 么?
b.set()把 b 中所有二进制位都置为 1
b.set(pos)把 b 中在 pos 处的二进制位置为 1
b.reset()把 b 中所有二进制位都置为 0
b.reset(pos)把 b 中在 pos 处的二进制位置为 0
b.flip()把 b 中所有二进制位逐位取反
b.flip(pos)把 b 中在 pos 处的二进制位取反
b.to_ulong()用 b 中同样的二进制位返回一个 unsigned long 值
os << b把 b 中的位集输出到 os 流
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值