? ? ? ?很多应用程序都需要处理字符串。C语言在string.h(在++中为cstring)中提供了一系列的字符串函数,很多早期的C++实现为处理字符串提供了自己的类。
? ? ? ? string类是由头文件string支持的(以头文件string.h和cstring支持对C风格字符串进行操纵的C库字符串函数,但不支持string类)。要使用类,关键在于知道它的公有接口,而string类包含大量的方法,其中包括了若干构造函数,用于将字符串赋给变量、合并字符串、比较字符串和访问各个元素的重载运算符以及用于在字符串中查找字符和子字符串的工具等。以string类包含的内容很多。
? ? ? ?先来看string的构造函数。毕竟,对于类而言,最重要的内容之一是,有哪些方法可用于创建其对象。程序清单1使用了string的7个构造函数(用ctor标识,这是传统C++中构造函数的缩写)。表1简要地描述了这些构造函数,它首先按顺序简要描述了程序清单1使用的7个构造函数,然后列出了C++11新增的两个构造函数。使用构造函数时都进行了简化,即隐藏了这样一个事实:string实际上是模板具体化basic_string<char>的一个typedef,同时省略了与内存管理相关的参数。 size_type是一个依赖于实现的整型,是在头文件string中定义的。string类将string::npos定义为字符串的最大长度,通常为unsigned int的最大值。以表格中使用缩写NBTS(null终止string)来表示以空字符结束的字符串一一传统的C字符串。
表1 string类的构造函数
构 造 函 数 | 描述 |
---|---|
string(const char * s) | 将string对象初始化为s指向的NBTS |
string(size_type n, char c) | 创建一个包含n个元素的string对象,其中每个元素都被初始化为字符c |
string(const string & str) | 将一个string对象初始化为string对象str(复制构造函数) |
string() | 创建一个默认的string对象,长度为0(默认构造函数) |
string(const char * s, size_type n) | 将string对象初始化为s指向的NBTS的前n个字符,即使超过了NBTS结尾 |
template<class Iter> | 将string对象初始化为区间[begin, end)内的字符,其中begin和end的行为就像指针,用于指定位置,范围包括begin在内,但不包括end |
string(const string & str, size_type pos, size_type n = npos) | 将一个string对象初始化为对象str中从位置pos开始到结尾的字符,或从位置pos开始的n个字符 |
string(string && str) noexcept | 这是C++11新增的,它将一个string对象初始化为string对象str,并可能修改str(移动构造函数) |
string(initializer_list<char> il) | 这是C++11新增的,它将一个string对象初始化为初始化列表il中的字符 |
#include <iostream>
#include <string>
// using string constructors
int main()
{
using namespace std;
string one("Lottery Winner!"); // ctor #1
cout << one << endl; // overloaded <<
string two(20, '$'); // ctor #2
cout << two << endl;
string three(one); // ctor #3
cout << three << endl;
one += " Oops!"; // overloaded +=
cout << one << endl;
two = "Sorry! That was "; //will Clean up the original string in object two
three[0] = 'P';
string four; // ctor #4
four = two + three; // overloaded +, =
cout << four << endl;
char alls[] = "All's well that ends well";
string five(alls,20); // ctor #5
cout << five << "!\n";
string six(alls+6, alls + 10); // ctor #6
cout << six << ", ";
string seven(&five[6], &five[10]); // ctor #6 again
cout << seven << "...\n";
cout << "Now four is :" << four << endl;
string eight(four, 7, 16); // ctor #7
cout << eight << " in motion!" << endl;
system("pause");
return 0;
}
程序清单1 的输出:
Lottery Winner!
$$$$$$$$$$$$$$$$$$$$
Lottery Winner!
Lottery Winner! Oops!
Sorry! That was Pottery Winner!
All's well that ends!
well, well...
Now four is :Sorry! That was Pottery Winner!
That was Pottery in motion!
Press any key to continue . . .
有哪些输入方式可用呢? 对于C风格的字符串, 有3种方式:
char info[100];
cin >> info; // read a word
cin.getline(info, 100); // read a line, discard \n
cin.get(info, 100); // read a line, leave \n in queue
对于string 对象 有两种方式:
string stuff;
cin >> stuff; // read a word
getline(cin, stuff); // read a line, discard \n
两个版本的getline() 都有一个可选参数, 用于指定使用什么字符作为读取的边界;
cin.getline(info,100,':'); // read up to :, discard :
getline(cin,stuff, ':'); // read up to :, discard :
对于string版本的getline() 能够自动调整目标string 对象的大小, 使之刚好能存储输入的字符:
char fname[10];
string lname;
cin >> fname; // could be a problem if input size > 9 characters
cin >> lname; // can read a very, very long word
cin.getline(fname, 10); // may truncate input
getline(cin, fname); // no truncation
自动调整大小的功能让 string版本的getline() 不需要指定要读取多少个字符的参数
string 版本的 getline() 从输入流中读取字符, 并将其放入目标string 中, 直到发生下面几种情况:
eofbit fail()等与 流状态 相关, 将在 ~ C++输入输出和文件 -> 三. 使用cin进行输入 -> 2. 流状态 ~ 中讲解
字符串比较
string 类对全部6个关系运算符都进行了重载, 如果机器排列序列为ASCII码, 那么数字字符 < 大写字符 < 小写字符;
对于每个关系运算符, 都以三种方式被重载, 以便将string对象和 另一个string对象,C风格字符串 进行比较 :
#include <iostream>
#include <exception>
int main()
{
using namespace std;
string snake1("cobra");
string snake2("coaal");
char snake3[20] = "cobra";
if (snake1 < snake2) // operator<(const string &, const string &)
{
cout << "snake1 < snake 2" << endl;
}
if (snake1 == snake3) // operator==(const string &, const char *)
{
cout << "snake1 == snake3" << endl;
}
if (snake3 != snake2) // operator!=(const char *, const string &)
{
cout << "snake3 != snake2" << endl;
}
system("pause");
return 0;
}
size() 和 length() 都返回字符串的字符数
length()成员来自较早版本的string类, 而size()则是为STL兼容性而添加的
字符串查找
string::npos是字符串可存储的最大字符数, 通常是无符号int或long的最大取值;
方 法 原 型 | 描 述 |
---|---|
size_type find(const string & str, | 从字符串的pos位置开始,查找子字符串str。如果找到,则返回该子字符串首次出现时其首字符的索引;否则,返回string :: npos |
size_type find(const char * s, | 从字符串的pos位置开始,查找子字符串s。如果找到,则返回该子字符串首次出现时其首字符的索引;否则,返回string :: npos |
size_type find(const char * s, | 从字符串的pos位置开始,查找s的前n个字符组成的子字符串。如果找到,则返回该子字符串首次出现时其首字符的索引;否则,返回string :: npos |
size_type find(char ch, | 从字符串的pos位置开始,查找字符ch。如果找到,则返回该字符首次出现的位置;否则,返回string :: npos |
int where = snake1.find_first_of("hark");
find_last_of()方法的功能与此相同,只是它查找的是最后一次出现的位置。因此,下面的语句返回a在“cobra”中的位置:
int where = snake1.find_last_of("hark");
find_first_not_of()方法在字符串中查找第一个不包含在参数中的字符,因此下面的语句返回c在“cobra”中的位置,因为“hark”中没有c:
int where = snake1.find_first_not_of("hark");
很多, 就萝莉一些, 再挑几个讲. 其他的用到的时候就知道了
a) =,assign() //赋以新值
b) swap() //交换两个字符串的内容
c) +=,append(),push_back() //在尾部添加字符
d) insert() //插入字符
e) erase() //删除字符
f) clear() //删除全部字符
g) replace() //替换字符
h) + //串联字符串
i) ==,!=,<,<=,>,>=,compare() //比较字符串
j) size(),length() //返回字符数量
k) max_size() //返回字符的可能最大个数
l) empty() //判断字符串是否为空
m) capacity() //返回重新分配之前的字符容量
n) reserve() resize()//保留一定量内存以容纳一定数量的字符
//resize() 会重置size和length 参数如果小于当前的size,则截短,大于则扩充
//resize() 扩充时默认是用'\0'填充, 第二个可选参数可指定用什么字符填充
//resize()减少size时 不会影响capacity
o) [], at() //存取单一字符 at()索引无效时,会抛出out_of_range异常
p) >>,getline() //从stream读取某值
q) << //将谋值写入stream
r) copy() //将某值赋值为一个C_string
s) c_str() //将内容以C_string返回 不可修改
t) data() //将内容以字符序列指针形式返回 可修改
u) substr() //返回某个子字符串
v)查找函数
w)begin() end() //提供类似STL的迭代器支持
x) rbegin() rend() //逆向迭代器
y) get_allocator() //返回配置器
//string 中 capacity 最小 15
z) shrink_to_fit() //A non-binding request to reduce capacity() to size()
compare 返回值意义(吾用区间表示法表示):[小于,0,大于] , 0:相等
string s("abcd");
s.compare("abcd"); //返回0
s.compare("dcba"); //返回一个小于0的值
s.compare("ab"); //返回大于0的值
s.compare(s); //相等
//参数1:下标 2:字符个数 3:比较的对象 4:下标 5:字符个数
s.compare(0,2,s,2,2); //用"ab"和"cd"进行比较 小于零
//参数1:下标 2:字符个数 3:比较的对象 4:字符个数
s.compare(1,2,"bcx",2); //用"bc"和"bc"比较。
assign 重新分配
s.assign(str); //字面意思
//参数1:目标 2:下标 3:字符数
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’ ‘’赋给字符串
s.assign(5,'x');//把五个x赋给字符串
append 附加
s.append(str);
s.append(str,1,3);//不解释了 同前面的函数参数assign的解释
s.append(str,2,string::npos)//
s.append("my name is jiayp");
s.append("nico",5);
s.append(5,'x');
s.push_back('a');//这个函数只能增加单个字符
insert 插入
s.insert(0,"my name");
s.insert(1, "m");
s.insert(1,str);
replace erase 替换和擦除
s.replace(1,2,"nternationalizatio");//从索引1开始的2个替换成后面的C_string或string对象
s.erase(13);//从索引13开始往后全删除
s.erase(7,5);//从索引7开始往后删5个
substr 返回子字符串(新的string)
s.substr();//返回s的全部内容
s.substr(11);//从索引11往后的子串
s.substr(5,6);//从索引5开始6个字符
copy 复制并替换目标中原有的字符
char str1[20] = "Hello";
char str2[20] {0};
string sl = "World";
//参数1:目标对象 2:要copy的字符数 3:从sl的下标?开始
sl.copy(str1, 5, 2);//
cout << str1 << endl;
方法capacity()返回当前分配给字符串的内存块的大小,而reserve()方法让您能够请求增大内存块
#include <iostream>
#include <string>
int main()
{
using namespace std;
string empty;
string small = "bit";
string larger = "Elephants are a girl's best friend";
cout << "Sizes:\n";
cout << "\tempty: " << empty.size() << endl;
cout << "\tsmall: " << small.size() << endl;
cout << "\tlarger: " << larger.size() << endl;
cout << "Capacities:\n";
cout << "\tempty: " << empty.capacity() << endl;
cout << "\tsmall: " << small.capacity() << endl;
cout << "\tlarger: " << larger.capacity() << endl;
empty.reserve(50);
cout << "Capacity after empty.reserve(50): "
<< empty.capacity() << endl;
return 0;
}
如果您有string对象,但需要C风格字符串,该如何办呢?
string filename;
filename.c_str(); //返回c风格字符串
如果你需要string对象提供一个可供修改内容字符串的地址 以便传入需要修改该地址上C风格字符串的函数
可以使用data()成员函数返回可修改的C字符串地址, 但你必须十分小心:很多成员函数行为都将变异
s.append(s.data());
能使它的成员恢复正常的方法#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <memory>
#include <iomanip>
#include <vector>
using namespace std;
int main()
{
string buf;
cout << setw(16) << left;
cout << setw(16) << "before buf size():" << buf.size() << endl;
cout << setw(16) << "before buf capacity():" << buf.capacity() << endl;
cin.get(buf.data(), 100);
// buf.append(buf.data());
// buf.assign(buf.data());
// buf = buf.data();
cout << setw(16) << "buf size():" << buf.length() << endl;
cout << setw(16) << "buf capacity():" << buf.capacity() << endl;
cout << setw(16) << "empty():" << boolalpha << buf.empty() << endl;
cout << endl;
cout << setw(16) << "buf:" << setw(20) << buf << "address :" << &buf << endl;
cout << setw(16) << "buf.c_str():" << setw(20) << buf.c_str() << "address :" << (int*)buf.c_str() << endl;
cout << setw(16) << "buf.data():" << setw(20) << buf.data() << "address :" << (int*)buf.data() << endl;
cout << setw(16) << "strlen(buf.c_str()):" << strlen(buf.c_str()) << endl;
cout << endl;
cout << "cin.fail():" << cin.fail() << endl;
cout << "cin.eof():" << cin.eof() << endl;
cout << "cin.bad():" << cin.bad() << endl;
system("pause");
return 0;
}
s.append(s.data());
是安全的string 对象中 data()和c_str()成员函数返回的是同一个地址, 它的所有成员都是用来限定string对象单独维护的一个字符串的, 而不是data()和c_str()返回的字符串. 如果说你在外部增加或修改了data()中的字符, string对象变现(指变成字符串)时 会按当前内部成员size截取 data()上的字符放入那个隐藏维护的字符串地址上 并在size+1处添加’\0’ .
本节将string类看作是基于char类型的。事实上,正如前面指出的,string库实际上是基于一个模板类的:
template<class charT, class traits = char _traits<charT>,
class Allocator = allocator<charT> >
basic_string {...};
模板basic_string有4个具体化(特化),每个具体化都有一个typedef名称:
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
typedef basic_string<char16_t> u16string; // C++11
typedef basic_string<char32_t> u32string ; // C++11
这让您能够使用基于类型wchar_t、char16_t、char32_t和char的字符串。甚至可以开发某种类似字符的类,并对它使用basic_string类模板(只要它满足某些要求)。traits类描述关于选定字符类型的特定情况,如如何对值进行比较。对于wchar_t、char16_t、char32_t和char类型,有预定义的char_traits模板具体化,它们都是traits的默认值。Allocator是一个管理内存分配的类。对于各种字符类型,都有预定义的allocator模板具体化,它们都是默认的。它们使用new和delete。
要创建智能指针, 必须包含头文件 memory
template<class X> class auto_ptr {
public:
explicit auto_ptr(X* p =0) throw();
...};
auto_ptr<double> pd(new double); // pd an auto_ptr to double
// (use in place of double * pd)
auto_ptr<string> ps(new string); // ps an auto_ptr to string
// (use in place of string * ps)
auto_ptr 已被弃用, 应使用功能更明确的shared_ptr 和 unique_ptr
成员方法名 | 功 能 |
---|---|
operator=() | 重载赋值号,使得同一类型的 shared_ptr 智能指针可以相互赋值。 |
operator*() | 重载 * 号,获取当前 shared_ptr 智能指针对象指向的数据。 |
operator->() | 重载 -> 号,当智能指针指向的数据类型为自定义的结构体时,通过 -> 运算符可以获取其内部的指定成员。 |
swap() | 交换 2 个相同类型 shared_ptr 智能指针的内容。 |
reset() | 当函数没有实参时,该函数会使当前 shared_ptr 所指堆内存的引用计数减 1,同时将当前对象重置为一个空指针;当为函数传递一个新申请的堆内存时,则调用该函数的 shared_ptr 对象会获得该存储空间的所有权,并且引用计数的初始值为 1。 |
get() | 获得 shared_ptr 对象内部包含的普通指针。 |
use_count() | 返回同当前 shared_ptr 对象(包括它)指向相同的所有 shared_ptr 对象的数量。 |
unique() | 判断当前 shared_ptr 对象指向的堆内存,是否不再有其它 shared_ptr 对象再指向它。 |
operator bool() | 判断当前 shared_ptr 对象是否为空智能指针,如果是空指针,返回 false;反之,返回 true。 |
空的 shared_ptr 指针,其引用计数为 0,而不是 1。
std::shared_ptr<int> p1; //不传入任何实参
std::shared_ptr<int> p2(nullptr); //传入空指针 nullptr
shared_ptr 模板还提供有相应的拷贝构造函数和移动构造函数,例如:
std::shared_ptr<int> p3 = std::make_shared<int>(10);//同p3(new int(10))
//调用拷贝构造函数
std::shared_ptr<int> p4(p3);//或者 std::shared_ptr<int> p4 = p3;
//调用移动构造函数
std::shared_ptr<int> p5(std::move(p4)); //或者 std::shared_ptr<int> p5 = std::move(p4);
shared_ptr 只对它自身类型的指针包裹器(我们把这种智能指针称作指针包裹器吧)统计引用计数:
int* ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr);//错误用法
shared_ptr<int> p3(ptr);//错误用法
成员函数名 | 功 能 |
---|---|
operator*() | 获取当前 unique_ptr 指针指向的数据。 |
operator->() | 重载 -> 号,当智能指针指向的数据类型为自定义的结构体时,通过 -> 运算符可以获取其内部的指定成员。 |
operator =() | 重载了 = 赋值号,从而可以将 nullptr 或者一个右值 unique_ptr 指针直接赋值给当前同类型的 unique_ptr 指针。 |
operator []() | 重载了 [] 运算符,当 unique_ptr 指针指向一个数组时,可以直接通过 [] 获取指定下标位置处的数据。 |
get() | 获取当前 unique_ptr 指针内部包含的普通指针。 |
get_deleter() | 获取当前 unique_ptr 指针释放堆内存空间所用的规则。 |
operator bool() | unique_ptr 指针可直接作为 if 语句的判断条件,以判断该指针是否为空,如果为空,则为 false;反之为 true。 |
release() | 释放当前 unique_ptr 指针对所指堆内存的所有权,但该存储空间并不会被销毁。 |
reset(p) | 其中 p 表示一个普通指针,如果 p 为 nullptr,则当前 unique_ptr 也变成空指针;反之,则该函数会释放当前 unique_ptr 指针指向的堆内存(如果有),然后开始维护 p 所指堆内存。可以这样用:reset(new int) |
swap(x) | 交换当前 unique_ptr 指针和同类型的 x 指针。 |
基于 unique_ptr 类型指针不共享各自拥有的堆内存原则,因此 C++11 标准中的 unique_ptr 模板类没有提供拷贝构造函数,只提供了移动构造函数。例如
std::unique_ptr<int> p4(new int);
std::unique_ptr<int> p5(p4);//错误,堆内存不共享
std::unique_ptr<int> p5(std::move(p4));//正确,调用移动构造函数
//指定 default_delete 作为释放规则
std::shared_ptr<int> p6(new int[10], std::default_delete<int[]>());
//自定义释放规则
void deleteInt(int*p) {
delete []p;
}
//初始化智能指针,并自定义释放规则
std::shared_ptr<int> p7(new int[10], deleteInt);
int* ptr = new int;
shared_ptr<int> p3(ptr);
shared_ptr p1(p3);
shared_ptr p2(p1);
weak_ptr wp(p1);// 注意 p1.reset(); 放在此句后面
p1.reset();
cout << boolalpha << endl;
cout << wp.use_count() << endl;//2
cout << p1.use_count() << endl;//0
成员方法 | 功 能 |
---|---|
operator=() | 重载 = 赋值运算符,是的 weak_ptr 指针可以直接被 weak_ptr 或者 shared_ptr 类型指针赋值。 |
swap(x) | 其中 x 表示一个同类型的 weak_ptr 类型指针,该函数可以互换 2 个同类型 weak_ptr 指针的内容。 |
reset() | 将当前 weak_ptr 指针置为空指针。 |
use_count() | 查看指向和当前 weak_ptr 指针相同的 shared_ptr 指针的数量。 |
expired() | 判断当前 weak_ptr 指针为否过期(指针为空,或者指向的智能指针记录的堆内存已经被释放)。 |
lock() | 如果当前 weak_ptr 已经过期,则该函数会返回一个空的 shared_ptr 指针;反之,该函数返回一个和当前 weak_ptr 指向相同的 shared_ptr 指针。(只是返回并不会增加引用计数) |
函数符的概念:
正如STL定义了容器和 iterator 的概念一样,它也定义了函数符(functor)的概念。
生成器(generator)是不用参数就可以调用的函数符。
一元函数(unary function)是用一个参数可以调用的函数符。
二元函数(binary function)是用两个参数可以调用的函数符。
例如,提供给for_each()的函数符应该是一元函数,因为它每次用于一个容器元素。当然,这些概念都有相应的改进版本:
返回bool值的一元函数是谓词(predicate).
返回bool值的二元函数是二元谓词(binary predicate).
下面的代码删除容器中所有大于100的元素
bool tooBig(int n){ return n > 100; }
list<int> scores;
...
scores.remove_if(tooBig);
二元函数可用函数对象包裹成一元函数
template<class T>
class TooBig
{
private:
T cutoff;
public:
TooBig(const T & t) : cutoff(t) {}
bool operator()(const T & v) { return v > cutoff; }
};
自定义函数适配器
//1、自定义unary_function
//只要类继承了my_unary_function类,就可以被下面的AdaptorNot适配。
template <typename arg, typename res>
struct my_unary_function
{
typedef arg argument_type;
typedef res result_type;//有这两个类型成员的称为自适应函数对象
};
template<class Arg, class Ret>
class my_fun: public my_unary_function<Arg, Ret>
{
my_fun() {};
Ret operator()(Arg arg)
{
Ret mybool;
//do something
return mybool;
}
};
//2、改写带模板的函数适配器
template <typename T>
class AdaptorNot
{
protected:
T opt;
public:
AdaptorNot(T x) : opt(x) {}
//重载了括号
typename T::result_type operator()(typename T::argument_type flag)
{
typename T::result_type ret;
//do something
return !opt(flag);
}
};
和其它容器不同,array 容器的大小是固定的
array<T,N> 类模板中,T 用于指明容器中的存储的具体数据类型,N 用于指明容器的大小,需要注意的是,这里的 N 必须是编译时能确定的常量,不能用变量表示。
std::array<double, 10> values {0.5,1.0,1.5,,2.0};
这里只初始化了前 4 个元素,剩余的元素都会被初始化为 0.0。
当两个 array 容器满足大小相同并且保存元素的类型相同时,两个 array 容器可以直接直接做赋值操作,即将一个容器中的元素赋值给另一个容器。
在满足以上 2 个条件的基础上,如果其保存的元素也支持比较运算符,就可以用任何比较运算符直接比较两个 array 容器。
成员函数 | 功能 |
---|---|
begin() | 返回指向容器中第一个元素的随机访问迭代器。 |
end() | 返回指向容器最后一个元素之后一个位置的随机访问迭代器,通常和 begin() 结合使用。 |
rbegin() | 返回指向最后一个元素的随机访问迭代器。 |
rend() | 返回指向第一个元素之前一个位置的随机访问迭代器。 |
cbegin() | 和 begin() 功能相同,只不过在其基础上增加了 const 属性,不能用于修改元素。 |
cend() | 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
crbegin() | 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
crend() | 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
size() | 返回容器中当前元素的数量,其值始终等于初始化 array 类的第二个模板参数 N。 |
max_size() | 返回容器可容纳元素的最大数量,其值始终等于初始化 array 类的第二个模板参数 N。 |
empty() | 判断容器是否为空,和通过 size()==0 的判断条件功能相同,但其效率可能更快。 |
at(n) | 返回容器中 n 位置处元素的引用,该函数自动检查 n 是否在有效的范围内,如果不是则抛出 out_of_range 异常。 |
front() | 返回容器中第一个元素的直接引用,该函数不适用于空的 array 容器。 |
back() | 返回容器中最后一个元素的直接应用,该函数同样不适用于空的 array 容器。 |
data() | 返回一个指向容器首个元素的指针。利用该指针,可实现复制容器中所有元素等类似功能。 |
fill(val) | 将 val 这个值赋值给容器中的每个元素。 |
array1.swap(array2) | 交换 array1 和 array2?容器中的所有元素,但前提是它们具有相同的长度和类型。 |
std::vector<double> values(20);//values 容器开始时就有 20 个元素,它们的默认初始值都为 0。
std::vector<double> values(20, 1.0);//第二个参数指定了所有元素的初始值,因此这 20 个元素的值都是 1.0。
int num=20;
double value =1.0;
std::vector<double> values(num, value);
std::vector<char>value1(5, 'c');
std::vector<char>value2(value1);
int arr[]={1,2,3};
std::vector<int>values(arr, arr+2);//values 将保存{1,2}
std::vector<int>value1{1,2,3,4,5};
std::vector<int>value2(std::begin(value1),std::begin(value1)+3);//value2保存{1,2,3}
函数成员 | 函数功能 |
---|---|
begin() | 返回指向容器中第一个元素的迭代器。 |
end() | 返回指向容器最后一个元素所在位置后一个位置的迭代器,通常和 begin() 结合使用。 |
rbegin() | 返回指向最后一个元素的迭代器。 |
rend() | 返回指向第一个元素所在位置前一个位置的迭代器。 |
cbegin() | 和?begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
cend() | 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
crbegin() | 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
crend() | 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
size() | 返回实际元素个数。 |
max_size() | 返回元素个数的最大值。这通常是一个很大的值,一般是 232-1,所以我们很少会用到这个函数。 |
resize() | 改变实际元素的个数。 |
capacity() | 返回当前容量。 |
empty() | 判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。 |
reserve() | 增加容器的容量。 |
shrink _to_fit() | 将内存减少到等于当前元素实际所使用的大小。 |
operator[ ] | 重载了?[ ] 运算符,可以向访问数组中元素那样,通过下标即可访问甚至修改 vector 容器中的元素。 |
at() | 使用经过边界检查的索引访问元素。 |
front() | 返回第一个元素的引用。 |
back() | 返回最后一个元素的引用。 |
data() | 返回指向容器中第一个元素的指针。 |
assign() | 用新元素替换原有内容。 |
push_back() | 在序列的尾部添加一个元素。 |
pop_back() | 移出序列尾部的元素。 |
insert() | 在指定的位置插入一个或多个元素。 |
erase() | 移出一个元素或一段元素。 |
clear() | 移出所有的元素,容器大小变为 0。 |
swap() | 交换两个容器的所有元素。 |
emplace() | 在指定的位置直接生成一个元素。 |
emplace_back() | 在序列尾部生成一个元素。 |
还需注意的是,如果调用 reserve() 来增加容器容量,之前创建好的任何迭代器(例如开始迭代器和结束迭代器)都可能会失效,这是因为,为了增加容器的容量,vector<T> 容器的元素可能已经被复制或移到了新的内存地址。所以后续再使用这些迭代器时,最好重新生成一下。
树
,网
,链
,环
等结构成员函数 | 功能 |
---|---|
begin() | 返回指向容器中第一个元素的双向迭代器。 |
end() | 返回指向容器中最后一个元素所在位置的下一个位置的双向迭代器。 |
rbegin() | 返回指向最后一个元素的反向双向迭代器。 |
rend() | 返回指向第一个元素所在位置前一个位置的反向双向迭代器。 |
cbegin() | 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
cend() | 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
crbegin()? | 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
crend() | 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
empty() | 判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。 |
size() | 返回当前容器实际包含的元素个数。 |
max_size() | 返回容器所能包含元素个数的最大值。这通常是一个很大的值,一般是 232-1,所以我们很少会用到这个函数。 |
front() | 返回第一个元素的引用。 |
back() | 返回最后一个元素的引用。 |
assign() | 用新元素替换容器中原有内容。 |
emplace_front() | 在容器头部生成一个元素。该函数和 push_front() 的功能相同,但效率更高。 |
push_front() | 在容器头部插入一个元素。 |
pop_front() | 删除容器头部的一个元素。 |
emplace_back() | 在容器尾部直接生成一个元素。该函数和 push_back() 的功能相同,但效率更高。 |
push_back() | 在容器尾部插入一个元素。 |
pop_back() | 删除容器尾部的一个元素。 |
emplace() | 在容器中的指定位置插入元素。该函数和 insert() 功能相同,但效率更高。 |
insert()? | 在容器中的指定位置插入元素。 |
erase() | 删除容器中一个或某区域内的元素。 |
swap() | 交换两个容器中的元素,必须保证这两个容器中存储的元素类型是相同的。 |
resize() | 调整容器的大小。 |
clear() | 删除容器存储的所有元素。 |
splice() | 将一个 list 容器中的元素插入到另一个容器的指定位置。 |
remove(val) | 删除容器中所有等于 val 的元素。 |
remove_if() | 删除容器中满足条件的元素。 |
unique() | 删除容器中相邻的重复元素,只保留一个。 |
merge() | 合并两个事先已排好序的 list 容器,并且合并之后的 list 容器依然是有序的。 |
sort() | 通过更改容器中元素的位置,将它们进行排序。 |
reverse() | 反转容器中元素的顺序。 |
和序列式容器不同的是,关联式容器在存储元素时还会为每个元素在配备一个键,整体以键值对的方式存储到容器中。相比前者,关联式容器可以通过键值直接找到对应的元素,而无需遍历整个容器。另外,关联式容器在存储元素,默认会根据各元素键值的大小做升序排序。
相比其它类型容器,关联式容器查找、访问、插入和删除指定元素的效率更高
pair: 单个键值对容器 在#include <utility>中定义
pair kl("hello", 20);
map: 多个键值对容器, 并且能指定排序规则
使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改。
map 模板头:
template <typename _Key, typename _Tp, typename _Compare = std::less<_Key>,
typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
class map {...}
第三个参数是排序规则 默认是 std::less<_Key> 升序 一般是依据键 来排序
初始化:
std::map<std::string, int>myMap{ {"some c++ things", 10}, {"python things", 20} };
std::map newMap(++myMap.begin(), myMap.end());
cout << newMap.begin()->first << endl; //some c++ things
成员方法 | 功能 |
---|---|
begin() | 返回指向容器中第一个(注意,是已排好序的第一个)键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
end() | 返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
rbegin() | 返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
rend() | 返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
cbegin() | 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
cend() | 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
crbegin() | 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
crend() | 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
find(key) | 在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
lower_bound(key) | 返回一个指向当前 map 容器中第一个 >= key 的键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
upper_bound(key) | 返回一个指向当前 map 容器中第一个 > key 的键值对的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
equal_range(key) | 该方法返回一个包含 2 个双向迭代器 pair 对象,其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。 |
empty()? | 若容器为空,则返回 true;否则 false。 |
size() | 返回当前 map 容器中存有键值对的个数。 |
max_size() | 返回 map 容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。 |
operator[] | map容器重载了 [] 运算符,只要知道 map 容器中某个键值对的键的值的引用,就可以向获取数组中元素那样,通过键直接获取对应的值。如果容器中没有这个键,就增加这个键,其值:基本数据类型,则值为 0;如果是 string 类型,其值为 "",即空字符串 |
at(key) | 找到 map 容器中 key 键对应的值,如果找不到,该函数会引发 out_of_range 异常。 |
insert() | 向 map 容器中插入键值对。 |
erase() | 删除 map 容器指定位置、指定键(key)值或者指定区域内的键值对。后续章节还会对该方法做重点讲解。 |
swap() | 交换 2 个 map 容器中存储的键值对,这意味着,操作的 2 个键值对的类型必须相同。 |
clear() | 清空 map 容器中所有的键值对,即使 map 容器的 size() 为 0。 |
emplace() | 在当前 map 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。 |
emplace_hint() | 在本质上和 emplace() 在 map 容器中构造新键值对的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数。 |
count(key) | 在当前 map 容器中,查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1。 |
equal_range(key) 用法示例:
#include <iostream>
#include <utility> //pair
#include <map> // map
#include <string> // string
using namespace std;
int main() {
//创建并初始化 map 容器
std::map<string, string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
{"C语言教程","http://c.biancheng.net/c/"},
{"Java教程","http://c.biancheng.net/java/"} };
//创建一个 pair 对象,来接收 equal_range() 的返回值
pair <std::map<string, string>::iterator, std::map<string, string>::iterator> myPair = myMap.equal_range("C语言教程");
//通过遍历,输出 myPair 指定范围内的键值对
for (auto iter = myPair.first; iter != myPair.second; ++iter) {
cout << iter->first << " " << iter->second << endl;
}
return 0;
}
和 lower_bound(key)、upper_bound(key) 一样,该方法也更常用于 multimap 容器,因为 map 容器中各键值对的键的值都是唯一的,因此通过 map 容器调用此方法,其返回的范围内最多也只有 1 个键值对。
用[]修改pair 中的 second:
#include <iostream>
#include <map> // map
#include <string> // string
using namespace std;
int main() {
//创建空 map 容器
std::map<string, string>myMap;
myMap["STL教程"]="http://goodstl/somthingelse/";
myMap["Python教程"] = "http://goodcode/python/";
myMap["STL教程"] = "http://goodstl/stl/";//假设没有"STL教程"这个键的话,那么这就是添加操作
for (auto i = myMap.begin(); i != myMap.end(); ++i) {
cout << i->first << " " << i->second << endl;
}
return 0;
}
insert() 插入键值对; 也能以初始化列表的方式传入
std::map<string, string>myMap;
pair p{"somekey", "somevalue"};
auto ret = myMap.insert(p);//返回 std::pair<iterator, bool> 类型
cout << ret.second << endl;
cout << ret.first->first <<" "<<ret.first->second<< endl;
emplace() 或者 emplace_hint() 效率比 insert() 高;可直接用键,值 来传参
mymap.emplace("someKey", "http://c.someValue.net/");
注意: 可以用初始化列表初始化 但不能使用 pair 类型初始化
multimap<string, string>mymultimap{pair("zhello", "world")};
map<string, string>mymap{pair("zhello", "world")};
map<string, string>mymap(pair("zhello", "world"));//error
//这就意味着没有重载pair类型版本的构造函数, 而是在初始化列表版本的构造函数里对pair类型做处理
template<typename _Key, typename _Compare = std::less<_Key>,
typename _Alloc = std::allocator<_Key> >
class set
{
...
typedef _Key key_type;
typedef _Key value_type;
...
}
int sortNum[] {1, 8, 5, 4, 3};
auto sortNumSize = end(sortNum) - begin(sortNum);
for (int i = sortNumSize - 1; i >= 0; i--)
{
for (int j = i; j >= 0 ; --j)
{
if (sortNum[i] < sortNum[j]) //为true时交换
{
//交换a与b的位置
auto temp = sortNum[i];
sortNum[i] = sortNum[j];
sortNum[j] = temp;
}
}
}
for (int i = 0; i < sortNumSize; i++)
{
cout << sortNum[i] << "," ;
}
为true时交换, 从代码上看, 如果是sortNum[i] < sortNum[j]
则是升序, 而如果是sortNum[i] > sortNum[j]
则是降序. 完全可以把这部分代码独立出去 作为排序规则;