自学内容网 自学内容网

С++第十三节课 string初体验

一、string类的相关函数

string实际上也就是一个管理字符的顺序表!

如果我们需要遍历一个字符串,怎么实现?

我们可以通过下标访问操作符 + size实现字符串的遍历!

int main()
{
string s1("hello world");
// 遍历一个字符串
cout << s1 << endl;
for (size_t i = 0; i < s1.size(); i++)
{
cout << s1[i];
}
return 0;
}
  • '/0'不是一个有效字符,是一个标识字符串结束的特殊字符;
  • size()统计不计算/0的数量;
for (size_t i = 0; i <= s1.size(); i++)

当i = s1.size()的时候, 此时已经访问到/0,但是vs下的编译器不打印;

还可以通过下标引用操作符进行内容的修改:

int main()
{
string s1("hello world");
// 遍历一个字符串
//cout << s1 << endl;
for (size_t i = 0; i < s1.size(); i++)
{

cout << s1[i]++;
}
return 0;
}

这里,内置类型和自定义类型的[]实际意义不一样!

string s1("hello world");
char s3[] = "hello world";
s1[1]++;
s3[1]++;

二、迭代器

可以将iterator想象成指针,但是它不一定是指针!

  • begin获取一个字符串的迭代器;
  • end获取最后一个字符下一个位置的迭代器(/0);

iterator是一个像指针一样的类型!有可能是指针(字符串中是指针),有可能不是指针!

用法和指针几乎一致!

string::iterator it = s1.begin();
while (it != s1.end())
{
(*it)--;
++it;
}

可以通过修改迭代器的值,用法与指针类似;

注意点:范围for

// 范围for
// 通过范围for进行修改
for (auto& ch : s1)
{
ch++;
}
// 通过范围for进行打印
for (auto ch:s1)
{
cout << ch << " ";
}
return 0;
}
  • 范围for中的ch是s1对象的每个字符的拷贝,然后操作后会自动迭代;
  • 因为ch是s1对象的拷贝,因此我们需要加上引用,否则对ch的修改无法影响s1;

范围for的底层实际上就算迭代器!自动迭代实际上就算++来实现的!

范围for会调用begin和end来实现工作!

因此,一个类不支持迭代器就不会支持范围for(例如栈stack)!

任何的容器都支持迭代器,并且用法是相似的!

vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(14);

vector<int>::iterator vit = v1.begin();
while (vit != v1.end())
{
cout << *vit << " " ;
vit++;
}

 总结:

  • iterator提供了一种统一的方式访问和修改容器的数据;
  • 算法(algorithm)可以通过迭代器处理容器中的数据;

上面是采用隐式实例化,根据不同的参数调用不同的函数;在这里构成了函数重载;

如果我们想要逆向遍历一个字符串

  • 可以取反向迭代器
string s1("hello world");
string::reverse_iterator rit = s1.rbegin();
while (rit != s1.rend())
{
cout << *(rit);
rit++;
}
}

  • 此时,反向迭代器rbegin指向字符串的最后一个字母;
  • rend指向字符串的前一个位置;

同时,有了auto可以省略的类型:

string s1("hello world");
//string::reverse_iterator rit = s1.rbegin();
auto rit = s1.rbegin();

分析下面的代码:

void Func(const string& s)
{
string::iterator it = s.begin();
while (it != s.end())
{
(*it)--;
++it;
}
}

这个代码无法编译通过!

这是因为const不能使用普通的迭代器,要使用const迭代器!(相当于权限的放大)

普通迭代器可以读可以写,但是const迭代器只能读(不能修改)!

同理,对于const rbegin来说只能读不能写!

共有上面四种迭代器;

string因为历史的原因,产生的比STL要早一些,因为之前string的长度都是length,为了和其他的SLT一致,引入size,其功能与strlen一样!

三、算法初体验

 max_size在不同编译器运行的结果不一样,因此在实际使用上没有什么用处;

capacity是返回当前字符串的存储空间,但是对于不同的编译环境下capacity的数值是不一样的! 

相同的代码,在vs下的capacity的值为15,在g++的编译条件下为11!(这是因为两个编译器用的STL的版本不一样!)

我们可以通过下面的代码查看vs下的扩容机制:

每次扩容大概为原来的1.5倍!

同样的代码,我们来查看Linux系统下的扩容机制:

g++下几乎为2倍扩容!

  • vs的19扩容机制:每次默认给15(实际上是16,省去后面的/0),然后每次扩容为原来的1.5倍;
  • g++的扩容机制:当前字符串长度为多少,即默认扩容数为多少,每次扩容到原来的2倍;

clear的作用:清除字符串的内容,将字符串改为空字符串;

分析下面的代码:

string s1("hello world");
    cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.clear();
cout << s1.size() << endl;
cout << s1.capacity() << endl;

可以发现vs2022的扩容机制是默认给定的capacity是字符串的长度!

且clear只是将字符串中的有效字符清除,但是容量capacity不发生改变!

同样的,Linux环境下也是将size清为0,capacity不发生改变!

四、练习题:字符串相加

. - 力扣(LeetCode)

两个整形相加,一个整型最大值为42亿多,如果我们需要相加的数字比42亿还多,可以采用字符串相加!

class Solution {
public:
    string addStrings(string num1, string num2) {
    int end1 = num1.size()-1;
    int end2 = num2.size()-1;
    string Strret;
    int carry = 0;  // 定义进位值
    while(end1 >=0 || end2 >=0)
    {
        int val1 = end1 >= 0 ? num1[end1]-'0':0 ;
        int val2 = end2 >= 0 ? num2[end2]-'0':0 ;
        int ret = val1 + val2 + carry;  // 当前位数相加之和; 
        carry = ret / 10;
        ret = ret % 10;

        // Strret += ('0'+ret);  // 因为是从后往前算,这里是用尾插
        Strret.insert(Strret.begin(),'0'+ret);  // 采用头插
        
        --end1;
        --end2;
    }
    // 出循环后如果还需要进位
    if (carry == 1)
    {
        //Strret +='1';
        Strret.insert(Strret.begin(),'1');
    }

        //reverse(Strret.begin(),Strret.end());
        return Strret;
    }
};

但是需要注意的是,头插的效率比尾插的效率低!

 

tong图像 小部件


原文地址:https://blog.csdn.net/weixin_47702917/article/details/142373275

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!