
༺ 个人主页 · 纪念229 ༻
༒专栏目录:《算法》༒
༒专栏目录:《C++》༒
༒专栏目录:《MySQL数据库》༒
༒专栏目录:《前端开发》༒
༺世上本没有路,走的人多了自然就有了༻
本文讲述的是C++ string根据代码介绍string,希望对你有所帮助
文章目录
1.模板代码补充
std是一种类,string是std的成员变量我们可以把它看作变长字符串数组
前面会补充一点模板的知识
c
////类模板
//typedef double STDataType;
//
//template<typename T>
//class Stack
//{
//public:
// Stack(size_t capacity = 4)
// {
// _array = new T[capacity];
// _capacity = capacity;
// _size = 0;
// }
// void Push(const T& x);
// void Pop();
//private:
// T* _array;
// size_t _capacity;
// size_t _size;
//};
//
//template<typename T>
//void Stack<T>::Push(const T& x)
//{
// //...扩容
// _array[_size] = x;
// _size++;
//}
//
//template<typename T>
//void Stack<T>::Pop()
//{}
这里要讲的就是类模板和类模板的成员函数的结构
类模板:
c
template <class/typename T>
类结构
类模板的成员函数在类外定义
c
template <class/typename T>
返回值 类名<类型名>:: 函数名 ()
{
......
}
主要讲类模板的成员函数在类外定义
在目标上写一个与类模板一样的模板,在函数名前面加类名<类型名>::
```c
//int main()
//{
// Stack<int> st1;
// //显式实例化
// 类模板,目标前加 template class
// 函数模板,目标前加 template
//
// Stack<double> st2;
//
// st1.Push(1);
// st1.Push(2);
// st1.Push(3);
//
// st2.Push(1.1);
// return 0;
//}
这里讲调用模板类和模板函数
显示实例化:
模板类
template class 类名<类型名> 对象;
模板函数
template 函数名();
其它的调用模板类/函数都是隐式实例化
2.string代码讲解
c
//string
//int main()
//{
// char str1[] = "ascii";
// char str2[] = "比特";
// //这个调试看不到的原因是char元素只有一个字节所以调试看不到三字节的汉字字符只能看到'?'
// char指针也不行元素装不下3字节的汉字字符
// str2[1]++;
// str2[1]++;
// str2[1]++;
// char a = 'A';
// //char a = "张";
// //为啥报错,一个字符用''char类型装不了汉字字符
// //汉字字符装3个字节
// //char类型只装一字符的3字符的字符会报错
// return 0;
//}
c
char str1[] = "ascii";
// char str2[] = "比特";
// //这个调试看不到的原因是char元素只有一个字节所以调试看不到三字节的汉字字符只能看到'?'
// char指针也不行元素装不下3字节的汉字字符
// str2[1]++;
// str2[1]++;
// str2[1]++;
重点将这个在字符数组中放汉字字符
我们会发现在调试里面看中文字符看的是乱码
这不是编译器的问题是因为常见的大小写英文字符,数字字符都是一个字节放在字符数组中的元素里(每个元素只可容纳1字节)而汉字字符一个3字节要三个元素框中的三个一节字符拼凑成一个汉字字符所以是乱码(在调试里面看汉字字符是一种愚蠢的行为要看直接在终端打印出来即可)
c
//class string
//{
//private:
// char* _str;
// size_t _size;
// size_t _capacity;
//};
这个是我们自己定义的string类实际上C++标准库里有头文件#include有了它我们就可以调用std与里的类string(它比我们自定义的string的成员函数和成员变量丰富得多)
所以用string(类)要加头文件 #include
c
int main()
{
//test_string1();
//test_string2();
//test_string3();
//test_string4();
//test_string5();
//test_string6();
test_string7();
//string类型要写头文件#include<string>srting类里的成员函数当然可以在string对象上使用
return 0;
}
接下来我会讲解几个自定义的函数来介绍string类里的成员函数
没讲完的下一篇文章我会接着讲解
c
//void test_string1()
//{
// //向前兼容
// string s1;
// string s2("hello world");
//
// //默认构造函数分为两种一种实例化啥都不写对象得到的是随机值
// //一种是直接实例化放字面量对象得到的是该初始化值
//
// //用的是C++自带类不是写的string类
// //自己写的类成员函数复杂且没有给指针开空间
//
// //当string类与头文件都存在时
// //编译器会优先用我们定义的类
// //这就是string s2("hello world");报错的原因
// string s3(s2);
// //string是类头文件是#include<string>
//
//
// //首先声明string类型的字符串下标从0开始
// //我们把string类型对象看作数组
// string s4(s2, 1, 6);
// //从s2下标1开始取6个元素给对象s4
// string s5(s2, 1, 60);
// //从s2下标1开始取60个元素给对象s5
// //s2的元素取完了就停止
// string s6(s2, 1);
// //从s2下标1开始把所有元素取得给s6
// const char* str = "hello world";
// string s7(str, 5);
// string s8(100, '#');
// //给对象s8赋值100个字符'#'
//
// cout << s1 << endl;
// cout << s2 << endl;
// cout << s3 << endl;
// cout << s4 << endl;
// cout << s5 << endl;
// cout << s6 << endl;
// cout << s7 << endl;
// cout << s8 << endl;
//
// s1 = s2;
// s1 = "xxxx";
// s1 = "x";
//}
c
// string s1;
// string s2("hello world");
C++标准库里的string有自己的构造函数让字符串为空
我们看到这两个都是调用了构造函数(因为s1、s2都是类类型)
string类型的字符数组与字符数组最大区别就是调试的时候string对象可以看到整条字符串而字符数组只可以看到一块一块的字符
string对象的字符串末尾自动加'\0'
当string自定义类与头文件#include都存在时编译器会优先用自定义的string类
string s3(s2);
这里就是对象调用拷贝构造
首先我们可以把string对象看作字符数组(当然对象里还有其它成员变量)它的下标从0开始
c
string s4(s2, 1, 6);
// //从s2下标1开始取6个元素给对象s4
// string s5(s2, 1, 60);
// //从s2下标1开始取60个元素给对象s5
// //s2的元素取完了就停止
// string s6(s2, 1);
// //从s2下标1开始把所有元素取得给s6
这里都是拷贝构造
// string s4(s2, 1, 6);
这个是从s2字符数组的下标1开始取6个字符给s4
// string s5(s2, 1, 60);
这个是从s2字符数组的下标1开始取60个字符给s5,如果取完了就停止
// string s6(s2, 1);
这个是从s2字符数组的下标1开始往后取完有效字符给s6
// string s8(100, '#');
这个调用的是构造函数取100个字符'#'给s8
c
s1 = s2;
// s1 = "xxxx";
// s1 = "x";
因为s1是类类型所以这些是拷贝重载和复制重载C++自带的string类有这些成员函数
c
void test_string2()
{
string s1("hello world");
const string s2("hello world");
s1[0]++;
////这里不要想着是++重载它不是类是字符元素
////纯属改ascll值
//s2[0]++;
cout << s1 << endl;
////遍历修改
////1、下标 + [] (小众)
//// s1的每个字符都++
for (int i = 0; i < s1.size(); i++)
////s1是string类型内置size函数(求字符串个数)
{
s1[i]++;
}
cout << s1 << endl;
////[begin(),end())
//// begin()指向第一个有效字符(下标0)
//// end()指向最后一个有效字符的下一位(末下标的下一位(不存在))
////
////
////
////2、迭代器 (所有容器主流遍历 + 加修改方式)
string::iterator it1 = s1.begin();
// //string::iterator(一个类型名)看作迭代器(智能指针)
// //s1.begin()调用s1的成员函数返回指向第一个字符的地址
while (it1 != s1.end())
//s1.end()调用s1的成员函数返回指向第最后字符的下一个东西的地址
{
(*it1)--;
++it1;
//因为it1是类类型所以这里调用了成员函数(解引用重载和加加重载)
}
cout << s1 << endl;
//string s = "你好";
//这个东西(s)在调试上看不到你好
//不用纠结,s实际上里面就是你好只是在监视窗口看不到想要看直接在终端打印即可
vector<int> v;
//用vector模板要带头文件#include<vector>
//vector(变长数组)(初始下标还是0)
//下面都是vector里的成员函数
v.push_back(100000);
v.push_back(2);
v.push_back(3);
vector<int>::iterator it2 = v.begin();
while (it2 != v.end())
{
cout << *it2 << " ";
++it2;
}
cout << endl;
//string类和下面的vector、list不一样它们调用的是类模板string调用的就是类,string就是vector<char>的进阶版,可以调用的成员函数更多
//string对象比vector<char>对象可视化更好在调试器里string对象看到的是"hello world"在vector<char>对象看到的是'h' 'e' 'l'......
list<int> lt;
//用list类模板要头文件#include<list>
//list双向链表
//下面就是list类可以用的成员函数
lt.push_back(10);
lt.push_back(20);
lt.push_back(30);
list<int>::iterator it3 = lt.begin();
while (it3 != lt.end())
{
cout << *it3 << " ";
it3++;
}
cout << endl;
//string调用头文件后用的是类
//vector和list调用头文件后用的是类模板
//这些东西都是容器
//迭代器
//类/模板类::iterator 这个iterator就是迭代器(指针/类)调用就要用类/类模板域
//这些类利用迭代器改变对象里成员变量里面的数据
//string和vector的迭代器是指针 list迭代器是类
//迭代器的意义
//1:统一类似的方式遍历修改容器
//2:算法脱离具体底层结构结构,和底层结构解耦,
//算法独立模板实现针对多个容器处理
reverse(s1.begin(), s1.end());
reverse(v.begin(), v.end());
reverse(lt.begin(), lt.end());
//reverse(起始指针,末指针)
//作用将该对象的成员变量中的数据根据始末指针进行交换
//头文件#include<algorithm>
auto x = 10;
auto y = 10.1;
cout << x << endl;
cout << y << endl;
//auto类型会推导相关类型
//不是模板
int& z = x;
auto& m = z;
m++;
auto p1 = &x;
//只能是指针
auto* p2 = &x;
//auto可以推导大多是类型(内置类型、指针类型、类类型等)
//这上面两个一个推导的是int*一个是int
//3、范围for C++11 语法糖
//自动取容器数据赋值给e
//自定判断结束
//自动迭代
//本质底层也会替换成迭代器, auto e = *迭代器
//for(char& e : s1)
for (auto& e : s1)
{
//e是迭代器的解引用
e--;
}
cout << endl;
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
int array[] = { 1,2,3,4,5 };
//我们看到int类型的数组要求个数要sizeof(数组名)/sizezof(数组名[0])
for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++)
{
cout << array[i] << " ";
}
cout << endl;
for (auto e : array)
{
cout << e << " ";
}
cout << endl;
//由此可见打印所有数组值用范围for更方便
}
s1[0]++;
这个s10是字符不是类加加不调用重载符纯属改该字符的sacll码值将它变成其它字符
////[begin(),end())
这里string对象里的begin()指向的是字符串第一个字符,end指向的是字符串最后一个有效字符(无效字符'\0'不算)的下一个位置(不存在)
c
////2、迭代器 (所有容器主流遍历 + 加修改方式)
string::iterator it1 = s1.begin();
// //string::iterator(一个类型名)看作迭代器(智能指针)
// //s1.begin()调用s1的成员函数返回指向第一个字符的地址
while (it1 != s1.end())
//s1.end()调用s1的成员函数返回指向第最后字符的下一个东西的地址
{
(*it1)--;
++it1;
//因为it1是类类型所以这里调用了成员函数(解引用重载和加加重载)
}
cout << s1 << endl;
//string s = "你好";
//这个东西(s)在调试上看不到你好
//不用纠结,s实际上里面就是你好只是在监视窗口看不到想要看直接在终端打印即可
iterator就是迭代器这里是string类里的指针(迭代器有类有/指针)所以调用要加string::(string域)
string::iterator it1 = s1.begin();
s1.begin()指的是s1字符串指向下标0位置的地址
s1.end()指的是s1字符串指向下标末位置下一个位置的地址
c
while (it1 != s1.end())
//s1.end()调用s1的成员函数返回指向第最后字符的下一个东西的地址
{
(*it1)--;
++it1;
//因为it1是类类型所以这里调用了成员函数(解引用重载和加加重载)
}
这里因为it1是指针 将it1解引用后减减改变该指针指向的字符
然后将it1加加向后移动继续改变字符串数组里其它字符直到it1到达s1.end()结束
c
vector<int> v;
//用vector模板要带头文件#include<vector>
//vector(变长数组)(初始下标还是0)
//下面都是vector里的成员函数
v.push_back(100000);
v.push_back(2);
v.push_back(3);
vector<int>::iterator it2 = v.begin();
while (it2 != v.end())
{
cout << *it2 << " ";
++it2;
}
cout << endl;
这里的vector是类模板而string是类 要用vecotr带头文件#include
因为是模板类所以数理化是要在类名后加<类型名>进行隐式实例化
vector<int> v;
这个对象调用构造函数是默认构造(修改过的)构造后里面的数据为空
vector模板类可以是几乎任何类型的数组是啥类型的实例化是由x决定string是优化版的vector都是字符数组但是string字符数组是
vector数组的优化版差异在成员函数string类比vector的成员函数多解决字符串的能力也就更强
文章到此就告一段落,希望对你有所帮助,感谢观看!