【C++】透析string类

小八哥向前冲~ 2024-09-20 13:05:02 阅读 56

                                个人主页: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经典题目

仅仅反转字母

题目:【力扣】仅仅反转字符

思路:

定义两个指针,一个在前,一个在后,当两个指针指向的都是字母的时候就开始交换!

代码:

<code>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;

}

};

字符串中的第一个字符

题目:字符串中的第一个字符

思路:

遍历字符串,利用映射原理将字符全部映射进一个数组,然后再次遍历字符串!

代码:

<code>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就能避免这个情况!

代码:

<code>int main() {

string s1;

getline(cin, s1, '\n');

size_t pos = s1.rfind(' ');

cout << s1.size() - (pos + 1) << endl;

return 0;

}

验证回文字符

题目:【力扣】验证回文字符

思路:

先将字符串转化成小写,定义双指针,一前一后,如果两指针指向的是字母或数字,开始判断是否一样!

代码:

<code>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;

}

};

字符串相加

题目:【力扣】字符串相加

思路:

我们按照数学里面的加减法进位的方式,从各自的字符串末尾开始相加,只是注意好进位,我们算好每位的数字,然后尾插进一个新字符串中,最终将它们翻转一下就行!

代码:

<code>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="")code>

{

_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;

}

}

好了,我们下期见!



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。