目录
1.模板
函数模板
函数模板的调用规则:
类模板
容器与迭代器
string的简单介绍
iterator(迭代器)
begin()与end()
rbegin()和rend()
Capacity(容量)
shrink_to_fit()
Element access(获取字符串中的元素)
Modifiers(修改字符串)
String opertations
其他
1.模板
什么是模板?模板能有什么用?
比如,你想写一个函数处理一些不同类型的数据,虽然数据的类型不同,但函数中处理数据的方法是一致的。如果不应用模板,那么就需要根据处理的数据类型写相应的仅仅只是参数类型不同的函数。
可以通过模板来实现泛式编程,大大减少程序中冗余的代码。
函数模板
例子:
int add(int a, int b)
{
return a + b;
}
double add(double a, double b)
{
return a + b;
}
int main()
{
int a = 1;
int b = 2;
double c = 2.1;
double d = 2.3;
add(a, b);
add(c, d);
return 0;
}
向要实现数据的加减,但加减的数据的类型多种多样需要写处理相应数据类型的函数,代码很容易冗余。
这是后就需要使用模板,
template<class T>
T add(T a, T b)
{
return a + b;
}
template是关键字,说明下面你写的一段代码是个模板
T是模板参数,如果你调用该函数时传的是int类型的数据,那么编译器会将T推导出来为int,并生成相应的参数为int,返回值为int的函数。
template<class T>
T add(T a, T b)
{
cout << sizeof(T) << endl;
return a + b;
}
int main()
{
int a = 1;
int b = 2;
double c = 2.1;
double d = 2.3;
add(a, b);
add(c, d);
return 0;
}
多模板参数
template<class T1,class T2>
//.......
函数模板的调用规则:
1.如果要处理的数据类型已经有实现的函数,那么就不会使用模板
int add(int a, int b)
{
cout << "已存在的处理int" << endl;
return a + b;
}
double add(double a, double b)
{
cout << "已存在的处理double" << endl;
return a + b;
}
template<class T>
T add(T a, T b)
{
cout << "模板函数" << endl;
return a + b;
}
int main()
{
int a = 1;
int b = 2;
double c = 2.1;
double d = 2.3;
add(a, b);
add(c, d);
return 0;
}
2. 如果没有现成的函数就使用模板生成一个该类型的模板
float e = 1.1;
float f = 1.2;
add(e, f);
3.如果传的参数无论是与已有函数还是模板参数(表面上(只看数据类型不考虑隐式类型转换))都不匹配,那么就可能报一些错误无法正常运行。
add(a, c);//传一个int,和一个double
如果调用的是已存在的函数,可能存在调用歧义的问题,以上的代码就有这个问题,因为我们实现了一个处理int的函数,一个处理double的函数。并且传的参数一个是int一个是double,如果将double强转为int那么就调用int函数,如果将int强转为double那么就调用double的函数,但不幸的是编译器无法确定强转的顺序的先后,所以有调用歧义,不能运行。
如果调用的是模板呢?
跟上面的解释差不多,无法确定是将int强转为double还是将int强转为double,所以模板无法推导出模板参数T的类型
解决方法:
就是确定对那个数据进行强制类型转换
add(a, (int)c);//将double强转为int
add((double) a, c);//将int强转为double
类模板
template<class T>
class A
{
private:
T _a;
};
A<int> a;//用int模板实例化
A<double> b;//用double模板实例化
类模板如何实例化出相应的对象的?
类模板会生成相应的类,在通过类的构造函数初始化对象
容器与迭代器
什么容器?什么又是迭代器?两者之间有什么关系?
容器(container):简单来说就是跟所学的数据结构一样,就是用来存储数据的。比如,顺序表,链表,堆等等。
迭代器(iterator):就是访问数据的工具
关系:迭代器可以用同一种方法来访问不同容器中的数据。
迭代器就是一个类似于指针的东西。
迭代器的底层也是一个类,但string和vector容器的iterator可能是指针。
迭代器的取值范围是左闭右开
迭代器的使用
string s = "hello world";
for (string::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it;
}
这个现在不懂没关系,下面会讲到。
string的简单介绍
链接:可以看更详细的使用说明
string - C++ Reference (cplusplus.com)
string是字符串类型
string的底层是一个类。
string姑且算一种容器吧,在C语言中没有string类型,但又字符数组就跟string差不多。
iterator(迭代器)
begin()与end()
begin返回string的起始迭代器
end返回string的结尾的迭代器
可以把返回值理解成类似于指针的东西。
使用:
int main()
{
string s = "hello world";
//第一种遍历一遍字符串
for (string::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it;
}
cout << endl;
//第二种遍历方式
string::iterator it = s.begin();
while (it != s.end())
{
cout << *it;
it++;
}
cout << endl;
//第三种遍历(范围for)
for (auto e : s)
{
cout << e;
}
return 0;
}
rbegin()和rend()
与begin()和end()不同的是rbegin()和rend(),返回值的类型不同,为反向迭代器reverse_iterator,
rbegin(),rend() | begin(),end() | |
返回值类型 | reverse_iterator(反向迭代器) | iterator |
遍历方式 | 从后往前遍历 | 从前往后遍历 |
Capacity(容量)
size():返回字符串的大小
length():跟size()的功能一样
capacity():返回容器的大小
max_size():返回能开辟的最大字符串的大小
resize()
改变字符串的size ,但如果想要改变后的大小大于容器的容量,那么就会先扩容,在改变字符的大小。
reserve()
可以扩容,但扩容只能往大的方向扩,如果你想扩容的大小小于等于原本容器的容量,那么就不会扩容。
vs下的扩容:
string s;
int oldcapacity = s.capacity();
cout << oldcapacity << endl;
for (int i = 0; i < 1000; i++)
{
s.push_back('a');
int newcapacity = s.capacity();
if (newcapacity != oldcapacity)
{
oldcapacity = newcapacity;
cout << newcapacity << endl;
}
}
由上图的代码和结果可知,容器的初始大小为15,之后差不多就是 1.5倍扩容。
clear()
将字符串变为一个空串(“”),但不改变容器的大小
empty()
判断该字符串是否是空串。
shrink_to_fit()
可以缩容,将容器缩小到适合字符串的大小可以减少不必要的空间浪费。
使用:
string s = "hello world";
cout << s.size() << endl;
cout << s.length() << endl;
cout << s.capacity() << endl;
cout << s.max_size() << endl;
//扩容
s.reserve(100);
cout << s.capacity() << endl;
s.resize(20);
cout << s.size() << endl;
//清理字符串中的内容
s.clear();
cout << s << endl;
//判空
cout << s.empty() << endl;
cout << s.capacity() << endl;
//缩容
s.shrink_to_fit();
cout << s.capacity() << endl;
Element access(获取字符串中的元素)
operator[ ]:可以像数组一样通过[ ]访问字符串中的元素。
string s("hello world");
for (int i = 0; i < s.size(); i++)
{
cout << s[i];
}
cout << endl;
at:获得指定位置的元素。
front:获得第一个元素。
back:获得最后一个元素。
Modifiers(修改字符串)
operator+=
在字符串之后追加字符串
string s;
s += "hello world";
string s1 = "aaaa";
s += s1;
s += 'b';
append
在字符串之后追加字符串
append没有重载的+=好用
第六个是模板,传的参数是迭代器。
assign :赋值
push_back
在字符串最后追加字符
string s;
s += "hello world";
s.push_back('a');
insert:向指定位置的字符串中插入数据
erase:删除指定位置的字符
string s("hello world");
string::iterator it = s.begin();
while (it != s.end())
{
cout << *it;
s.erase(it);
}
因为string的删除是,将指定位置后的字符都向前移动一位,所以不存在迭代器失效的问题。
replace:替换掉stringzhong指定的一段字符串
swap:交换两个字符串,这里的交换明面上交换的是字符串,实际上也交换了字符串的大小和容器的容量
pop_back:删除字符串最后的元素。
String opertations
c_str:返回字符串的首地址
copy: 将字符串中的数据从pos位置开始拷贝len个字符进s指向的数组中。
find:在字符串中找s指向的内容,并返回存在相应的位置。如果字符串中没有相应的内容,就返回 string::npos.
string::npos 就是string类中的一个静态变量,因为是size_t 所以初始化为-1,npos的只就是size_t的最大值。
返回npos的意思就是找到字符串末尾也没找到相应的内容。
rfind:相较于find,rfing就是从后往前找。
find_first_of:找到第一个字符既在字符串中出现,又在s指向的内容中。
find_last_of:相较于find_first_of,其从后往前找
find_first_not_of:从字符串中,找第一个在不在s指向的内容中出现的字符,
find_last_not_of:从后往前找
substr:返回字符串中,从pos位置开始len长度的字符串。
compare:比较两或某段字符串的 大小
其他
operator+:将左右字符串相加并返回一个新字符串(相加的结果)
lhs: left hand side(左操作数)
rhs:right hans side(右操作数)
relational operators:两个字符串之间的关系运算。
swap(std中):这个std的swap会在交换前会发生字符串的拷贝,浪费性能。
void swap(string& x, string& y)
{
string tmp = x;//tmp会拷贝构造
x = y;
y = x;
}
而string类中定义的swap不需要定义一个临时变量,直接就可以交换,效率更高。
重载的operator>> 和 operator<< 可以使流插入和流提取的对象直接为string类,跟方便使用。
getline可以从键盘中读入的字符插入字符串中 ,这里空格也可以读取,但operator>>不可以读取空格。