使用实际调用的函数不是同一个,因为不同类型参数,函数栈帧中开辟的空间不一样。参数不一样,所以调用的函数也不一样。使用模板速度必重载速度更快,因为是编译器直接生成。而STL就叫标准模板库。
template<class T>
void Swap(T& x1, T& x2) // 本意是用引用&
{
T x = x1;
x1 = x2;
x2 = x;
}
特别场景:当函数像求和一样,两个都用模板的T类型,那么T不可以同时为doble和int,如何解决这种问题?
答:显示实例化。这种方式是指定模板参数T的具体类型。
对于模板参数T:
可以做参数、也可能做返回值。
typedef中可以使用参数T使得某个类或某类数据结构支持尽可能多的参数类型,而只是用typedef int datatype不能同时满足两种或以上的数据结构存储的数据类型。实际上,面对不同参数,编译器会自动生成不一样的代码,不必自己写出来重复度极高的相似类或结构体的代码,工作都交给了编译器。如下写法:
stack<int> st1;
stack<double>st2;
stack<int*>st4;
以上,编译器实例化出几份代码分别是存int、double的等几个存不同元素类型的栈。
- 如果在类外使用类模板写函数,还要重声明使用了类模板T。不然如下第二行的T识别不出来。
- 使用类模板后,类型变成了class_name,class_name只是类名。
T fun(T x,T y){
return xx+yy;
}
A.fun(1, 2)
B.fun(1.0, 2)
C.fun(2.0, 1.0)
D.fun(1, 2.0)
1)template
2)template
3)template
4)template
5)template<typename T1,T2>
6)template
7)template
8)
STL是C++标准模板库(standard template libaray)。此外STL版本一般一致,常见有PJ、SGI版本等。
核心:容器、算法




既然有了下标搭配[]的用法,为什么还要迭代器呢?
因为其它STL也需要遍历,不能通过[]方式,为了保持统一。
这里发现:创建正、反迭代器类型it、rit都很费劲,右值是.begin()或.end(),用auto可以自动识别右值类型做转换。
当迭代器遇上const常字符串的引用:不能修改,只能*it读。
for(auto e: s1)
cout<<e<<endl;
- auto e只是对s1中的值做拷贝。如果要修改字符串,应该使用引用。
- 规定&必须放在e前面。
- 如下的auto可换char,但是不如auto好用。
for(auto& e: s1)
cout<<e<<endl;
可以扩容,也可以缩容。缩容可能让字符串丢失。
string file("test.txt");
FILE* fout = fopen(file, "W");
-------------下面可以的-----------------
string file2(test.txt);
FILE* fout = fopen(file_str, "W");
截取字符串,返回的结果是深拷贝。

s.substr(x, n):从下标为x起找n个。
param1:起始位置
param2:删个数。如果不给,就删完。
// 不空才去做
// find_first_not_of
void trimSpace(string& s)
{
if (!s.empty())
{
s.erase(0, s.find_first_not_of(" "));
s.erase(s.find_last_not_of(" ") + 1);
}
}
数字变字符串、字符串变数字:
stoi()转为int类型,stold转为double类型。

int main()
{
// s1 末尾默认有\0
string url = "https://www.baidu.com/?tn=62095104_31_oem_dg/";
size_t pos1 = url.find(":");
string protocol = url.substr(0, pos1 - 0);
cout << "协议:"<<protocol<< endl;
// 找第二个'/',find()和rfind()直接用都拿不到。
// 这就可以用find的其它用法:从某个位置开始找,我们根据url的规律,可以从w开始,pos1+3
size_t pos2 = url.find('/', pos1+3);
// cout <<"第二个/位置" << pos2 << endl; pos2 = 21
string domain = url.substr(pos1+3, pos2 - (pos1+3));
cout << "域名:"<<domain<<endl;
string uri = url.substr(pos2 + 1);
cout << "区分段:" << uri << endl;
// 删头
uri.erase(0, 1);
cout << "区分段:" << uri << endl;
// 删尾:从位置起删,如果是最后位置,则反着删
uri.erase(uri.size() - 1, 1);
cout << "区分段:" << uri << endl;
return 0;
}
#include
#include
#include
using namespace std;
int main() {
string s;
//char ch = getchar();
// 单个字符接收 以'\n'为结尾 会忽视空格
/*while (ch != '\n')
{
s += ch;
ch = getchar();
}*/
getline(cin, s);
int pos = s.rfind(' ');
string res = s.substr(pos + 1);
cout << res.size() << endl;
return 0;
}
cin>>s,
cin和scanf字符串默认以空格和换行结束。
当要输入字符串以空格间隔,只接收第一个。
string s;
char ch = getchar();
// 单个字符接收 以'\n'为结尾 会忽视空格
while (ch != '\n')
{
s += ch;
ch = getchar();
}
getline(cin, s);
思路:
利用左右变量begin、end,双重循环,依次交换头尾。外圈要符合begin
注意:
bool isLetter(char ch)
{
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
return true;
else
return false;
}
class Solution {
public:
string reverseOnlyLetters(string s) {
size_t begin = 0;
size_t end = s.size() - 1;
// 左到右判断一遍 遇到字母就停下 都是字母才交换。
// 注意换完还得往后走
while (begin < end)
{
while (begin < end && !isLetter(s[begin]))
{
begin++;
}
while (begin < end && !isLetter(s[end]))
{
end--;
}1
swap(s[begin], s[end]);
begin++;
end--;
}
return s;
}
};
思路:
利用计数排序思想,字母范围比较集中,st[i] - ‘a’ 结果为0~27,最后遍历寻找为1的值。
报错:

一直报错无返回值,仔细看因为左边提示:会有-1的结果。所以没有认真看题,不是所有都符合规范。
代码
class Solution {
public:
int firstUniqChar(string s) {
int countArr[26] = { 0 };
// 统计次数
for (size_t i = 0; i < s.size(); ++i)
countArr[s[i] - 'a']++;
for (int j = 0;j < s.size(); j++)
if (countArr[s[j] - 'a'] == 1)
return j;
return -1;
}
};
- 双指针法:begin、end同时挪动,是
为什么是两层:移动过程可能会停下做操作,做完操作后,因为外层循环使得继续去移动。所以两层循环搭配双指针非常合适。
2. 巧妙用法:大小写转换:对数字做tolower()和对字母做tolower()效果一样,不会影响数字,且比较字母更加方便。- 注意事项:当前都是字母后,比较完应该挪动指针。
class Solution {
public:
bool isLetterOrNum(char ch)
{
if (ch >= '0' && ch <= '9')
return true;
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
return true;
return false;
}
bool isPalindrome(string s) {
int begin = 0, end = s.size() - 1;
/*
*/
while (begin < end)
{
while (begin < end && !isLetterOrNum(s[begin]))
{
begin++;
}
while (begin < end && !isLetterOrNum(s[end]))
{
end--;
}
if (tolower(s[begin])!=tolower(s[end]))
{
cout << "这里不对" << endl;
return false;
}
else {
end--;
begin++;
}
}
return true;
}
};
Solution s1 = Solution();
-----------上面的可以,为什么下面不可以----------------
Solution s1 = new Solution();
-----------下面的又可以----------------
Solution* s1 = new Solution();
因为new在申请空间,返回地址,所以用类指针类型接收。
习惯上,操纵类喜欢用指针。