【C++】透析string类
个人主页:CSDN_小八哥向前冲~
所属专栏:C++入门
目录
string类介绍
auto和范围for
auto关键字
范围for
string类常用接口说明
string类常见构造
string类容量操作
string类的访问及遍历操作
string类修改操作
string的结构说明
vs下的结构
G++下的结构
string经典题目
仅仅反转字母
字符串中的第一个字符
字符串最后一个单词的长度
验证回文字符
字符串相加
string类的模拟实现
码源
string类介绍
关于string的详细介绍,我们可以去C++官网查询!
官网:https://cplusplus.com/
下面我只做一些简单介绍!
auto和范围for
auto关键字
- 在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个 不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型 指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期 推导而得。
- 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
- 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际 只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
- auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
- auto不能直接用来声明数组
范围for
- 对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此 C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围 内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。
- 范围for可以作用到数组和容器对象上进行遍历
- 范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。
使用:
string类常用接口说明
注意:这些所有函数可以去C++官网查看,有详细讲解!
string类常见构造
我们来详细演示一下!
string类容量操作
注意一下这些函数的要点!
我们依然来演示一下!
string类的访问及遍历操作
函数:
我们来演示一下!
string类修改操作
这些重要函数我们要了然于心!
演示一下!
string的结构说明
vs下的结构
真实的情况:
G++下的结构
我们来试试几个题目练习一下!
string经典题目
仅仅反转字母
题目:【力扣】仅仅反转字符
思路:
定义两个指针,一个在前,一个在后,当两个指针指向的都是字母的时候就开始交换!
代码:
class Solution {
public:
//判断是不是字母
bool isnumber(const char& tmp)
{
if(tmp<='z'&&tmp>='a') return true;
if(tmp<='Z'&&tmp>='A') return true;
return false;
}
string reverseOnlyLetters(string s) {
size_t begin=0,end=s.size()-1;
while(begin<end)
{
//找到字母
while(begin<end&&!isnumber(s[begin])) begin++;
while(begin<end&&!isnumber(s[end])) end--;
//交换字母
swap(s[begin++],s[end--]);
}
return s;
}
};
字符串中的第一个字符
题目:字符串中的第一个字符
思路:
遍历字符串,利用映射原理将字符全部映射进一个数组,然后再次遍历字符串!
代码:
class Solution {
public:
int firstUniqChar(string s) {
int count[26]={0};
//利用映射
for(auto& e:s) count[e-'a']++;
//再次遍历一遍数组
for(int i=0;i<s.size();i++)
{
if(count[s[i]-'a']==1) return i;
}
return -1;
}
};
字符串最后一个单词的长度
题目:【牛客】字符串最后一个单词的长度
思路:
注意如果我们用cin去输入,那么到了空格就会停下,我们用getline就能避免这个情况!
代码:
int main() {
string s1;
getline(cin, s1, '\n');
size_t pos = s1.rfind(' ');
cout << s1.size() - (pos + 1) << endl;
return 0;
}
验证回文字符
题目:【力扣】验证回文字符
思路:
先将字符串转化成小写,定义双指针,一前一后,如果两指针指向的是字母或数字,开始判断是否一样!
代码:
class Solution {
public:
bool isnumber(char& ch)
{
return (ch >= 'a' && ch <= 'z')
|| (ch >= '0' && ch <= '9');
}
bool isPalindrome(string s) {
//转换小写
for (auto& e : s)
{
if (e <= 'Z' && e >= 'A') e += 32;
}
int begin = 0, end = s.size() - 1;
while (begin < end)
{
while (begin < end && !isnumber(s[begin])) begin++;
while (begin < end && !isnumber(s[end])) end--;
if (s[begin++] != s[end--]) return false;
}
return true;
}
};
字符串相加
题目:【力扣】字符串相加
思路:
我们按照数学里面的加减法进位的方式,从各自的字符串末尾开始相加,只是注意好进位,我们算好每位的数字,然后尾插进一个新字符串中,最终将它们翻转一下就行!
代码:
class Solution {
public:
string addStrings(string num1, string num2) {
int end1=num1.size()-1,end2=num2.size()-1;
string s;
//进位变量
int next=0;
while(end1>=0||end2>=0)
{
int val1=end1>=0?num1[end1--]-'0':0;
int val2=end2>=0?num2[end2--]-'0':0;
int sum=val1+val2+next;
next=sum/10;
sum%=10;
s+=sum+'0';
}
if(next==1) s+='1';
//翻转
reverse(s.begin(),s.end());
return s;
}
};
string类的模拟实现
在这里我们重点说一下拷贝构造,赋值构造,字符串的输入。
拷贝构造
通常我们的拷贝构造是这样写的:
string(const string& s)
{
//传统写法
_str = new char[s._capacity];
_capacity = s._capacity;
_size = s._size;
strcpy(_str, s._str);
}
我们可以改善一下:
void swap(string& tmp)
{
std::swap(_str, tmp._str);
std::swap(_size, tmp._size);
std::swap(_capacity, tmp._capacity);
}
string(const string& s)
{
//传统写法
//_str = new char[s._capacity];
//_capacity = s._capacity;
//_size = s._size;
//strcpy(_str, s._str);
//现代写法
string tmp(s.c_str());
swap(tmp);
}
解释:构造一个和 s 相同的string类对象tmp,然后将tmp里面的数据和this里面的数据交换 ,最后无用数据tmp出作用域被销毁!
赋值构造也是如此!
传统写法:
string& operator=(string& s)
{
//传统写法
if (this != &s)
{
delete[] _str;
_str = new char[_capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
}
我们也可以相同的手法来改善它!
string& operator=(string& s)
{
//传统写法
//if (this != &s)
//{
// delete[] _str;
// _str = new char[_capacity + 1];
// strcpy(_str, s._str);
// _size = s._size;
// _capacity = s._capacity;
//}
//现代写法
if (this != &s)
{
string tmp(s.c_str());
swap(tmp);
}
return *this;
}
甚至可以进一步完善!
//或者(现代写法)
string& operator=(string tmp)
{
swap(tmp);
return *this;
}
构造一个新string 类对象这一步浓缩在了函数参数这一步!
字符串的输入jieshi
istream& operator>>(istream& in, string& s)
{
const size_t N = 256;
char buff[N] = { 0 };
char ch = 0;
ch = in.get();
int i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == N - 1)
{
buff[i] = '\0';
s += buff;//尾插
i = 0;//重置i
}
ch = in.get();
}
if (i > 0)//将剩下的数据尾插
{
buff[i] = '\0';
s += buff;
}
return in;
}
解释:我们相当于创建了一个数组容器,将字符不断插入进这个容器,当容器满的时候,将这些数据插入string 类对象里,如果暂停输入,将剩下的数据再插入进string 类对象里!
码源
String.h文件
#include<iostream>
#include<assert.h>
using namespace std;
namespace ywc
{
class string
{
public:
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
string(const char* str="")
{
_size = _capacity = strlen(str);
_str = new char[_size + 1];
strcpy(_str, str);
}
void swap(string& tmp)
{
std::swap(_str, tmp._str);
std::swap(_size, tmp._size);
std::swap(_capacity, tmp._capacity);
}
string(const string& s)
{
//传统写法
//_str = new char[s._capacity];
//_capacity = s._capacity;
//_size = s._size;
//strcpy(_str, s._str);
//现代写法
string tmp(s.c_str());
swap(tmp);
}
string& operator=(string s)
{
swap(s);
return *this;
}
const char* c_str() const
{
return _str;
}
size_t size()
{
return _size;
}
size_t capacity()
{
return _capacity;
}
char operator[](size_t pos)
{
assert(pos >= 0);
return _str[pos];
}
void reserve(size_t n);
void push_back(char ch);
string& append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);
string& insert(size_t pos, char ch);
string& insert(size_t pos, const char* str);
string& erease(size_t pos, size_t len);
size_t find(char ch, size_t pos = 0);
size_t find(const char* str, size_t pos = 0);
string substr(size_t pos, size_t len);
private:
char* _str = nullptr;
size_t _size = 0;
size_t _capacity = 0;
static const size_t npos;
};
ostream& operator<<(ostream& out, string& s);
istream& operator>>(istream& in, string& s);
}
String.cpp文件
namespace ywc
{
const size_t string::npos = -1;
void string::reserve(size_t n)
{
if (n > _capacity)
{
//"\0"
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void string::push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
_str[_size++] = ch;
_str[_size] = '\0';
}
string& string::append(const char* str)
{
size_t len = strlen(str);
if (len + _size >= _capacity)
{
reserve(len + _size > 2 * _capacity ?
len + _size : 2 * _capacity);
}
strcpy(_str + _size, str);
_size += len;
return *this;
}
string& string::operator+=(char ch)
{
push_back(ch);
return *this;
}
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
string& string::insert(size_t pos, const char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity = 0 ? 4 : 2 * _capacity);
}
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch;
_size++;
return *this;
}
string& string::insert(size_t pos, const char* str)
{
assert(pos < _size);
size_t len = strlen(str);
if (len + _size >= _capacity)
{
reserve(len + _size > 2 * _capacity
? len + _size : 2 * _capacity);
}
size_t end = len + _size;
while (end - len >= pos)
{
_str[end] = _str[end - len];
end--;
}
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = str[i];
}
_size += len;
return *this;
}
string& string::erease(size_t pos, size_t len)
{
assert(pos < _size);
if (len >= _size - pos)
{
_size = pos;
_str[pos] = '\0';
}
else
{
for (size_t i = pos;i<=_size-len;i++)
{
_str[i] = _str[i + len];
}
_size -= len;
}
return *this;
}
size_t string::find(char ch, size_t pos)
{
assert(pos < _size);
for (size_t i = 0; i < _size; i++)
{
if (_str[i] == ch)
return i;
}
return npos;
}
size_t string::find(const char* str, size_t pos)
{
assert(pos < _size);
char* tmp = strstr(_str + pos, str);
if (tmp == nullptr)
return npos;
else
return tmp - _str;
}
string string::substr(size_t pos, size_t len)
{
assert(pos < _size);
if (len > _size - pos)
{
len = _size - pos;
}
string sub;
sub.reserve(len);
for (size_t i = 0; i < len; i++)
{
sub += _str[pos + i];
}
return sub;
}
ostream& operator<<(ostream& out, string& s)
{
for (auto& ch : s)
{
cout << ch;
}
return out;
}
istream& operator>>(istream& in, string& s)
{
const size_t N = 256;
char buff[N] = { 0 };
char ch = 0;
ch = in.get();
int i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == N - 1)
{
buff[i] = '\0';
s += buff;//尾插
i = 0;//重置i
}
ch = in.get();
}
if (i > 0)//将剩下的数据尾插
{
buff[i] = '\0';
s += buff;
}
return in;
}
}
好了,我们下期见!