【C++】7.string

1.标准库的string类

  • string是表示字符串的字符串类
  • 在使用string类时,必须包含#include头文件以及using namespace std;
  • string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。

2.使用

1°四个默认成员函数

构造 析构 拷贝构造 赋值

void test_string1()
{
    string s1;//构造和析构 //ok
    string s2("hello");//字符串构造 //ok
    string s3("hello", 2);//拷贝前两个字符进行初始化
    string s4(s2);//拷贝构造 //ok
    string s5(s2, 1, 2);//从下标为1的位置开始拷贝两个字符
    string s6(s2, 1, string::npos);//或者想取8个 不会报错 一直拷贝完
    string s7(10, 'a');//10个a
    cout << s1 << endl;
    cout << s2 << endl;
    cout << s3 << endl;
    cout << s4 << endl;
    cout << s5 << endl;
    cout << s6 << endl;
    cout << s7 << endl;
    s1 = s7;//赋值 //ok
    cout << s1 << endl;
}

2°遍历+字符串拼接

  • operator[ ] [ ]里面为下标(类似于数组访问)

  • 迭代器

  • 范围for

void test_string2()
{
    string s1("hello");
    s1 += ' ';
    s1 += "world";
    cout << s1 << endl;
    //改 operator[] size []+下标
    for (size_t i = 0; i < s1.size(); ++i)
    {
        s1[i] += 1;
    }
    //读 operator[] size
    for (size_t i = 0; i < s1.size(); ++i)
    {
        cout << s1[i] << " ";
    }
    cout << endl;

    //迭代器 
    //写
    //string::iterator it = s1.begin();
    auto it = s1.begin();//auto也可
    while (it != s1.end())
    {
        *it -= 1;
        ++it;
    }
    //读
    it = s1.begin();
    while (it != s1.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
    //1.迭代器不一定是指针 像指针一样的东西
    //2.end是最后一个字符的下一个位置

    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    vector<int>::iterator vit = v.begin();
    while (vit != v.end())
    {
        cout << *vit << " ";
        ++vit;
    }
    cout << endl;

    //范围for
    //C++11
    for (auto ch : s1)
    {
        cout << ch << " ";
    }
    cout << endl;
}

3°迭代器

正向:begin->end

反向:rbegin->rend

int string2int(const string& str)//2->to
{
    int val = 0;
    //const迭代器 只能读 不能写
    string::const_iterator it = str.begin();//定义的时候要是const迭代器
    while (it != str.end())
    {
        val *= 10;
        val += (*it - '0');
        ++it;
    }
    //const反向
    //string::const_reverse_iterator rit = str.rbegin();//定义的时候要是const迭代器
    auto rit = str.rbegin();
    while (rit != str.rend())
    {
        val *= 10;
        val += (*rit - '0');
        ++rit;
    }
    return val;
}

//3.再看看其他迭代器 
//方向:正向和反向
//属性:普通和const
//感觉迭代器很难受
//长远看vector list map set deque 全部可以迭代器 很舒服
void test_string3()
{
    string s1("hello world");
    //倒着遍历? 反向迭代器
    string::reverse_iterator rit = s1.rbegin();
    while (rit != s1.rend())
    {
        cout << *rit << " ";
        ++rit;
    }
    cout << endl;
    //返回整形
    string nums("12345");
    cout << string2int(nums) << endl;
}

4°容量操作

string类对象的容量操作

size 返回字符串有效字符长度

length 返回字符串有效字符长度

capacity 返回空间总大小

empty 检测字符串释放为空串,是返回true,否则返回false

clear 清空有效字符

reserve 为字符串预留空间

resize 将有效字符的个数该成n个,多出的空间用字符c填充

//4.length size capacity
void test_string4()
{
    string s1("hello world");
    string s2("hello");
    cout << s1.size() << endl;
    cout << s2.size() << endl;
    cout << s1.length() << endl;
    cout << s2.length() << endl;

    //max_size()没有啥意义
    cout << s1.max_size() << endl;
    cout << s2.max_size() << endl;

    //capacity 增容的空间
    cout << s1.capacity() << endl;
    cout << s2.capacity() << endl;
    
    s1 += "111111";
    cout << s1.capacity() << endl;//基本是按倍数增加的
    
    //clear
    s1.clear();
    cout << s1 << endl;
    //size减到0了 但空间没有
    cout << s1.capacity() << endl;
}

//5.reverse resize
void test_string5()
{
    //string s;
    //s.reserve(100);//直接开够空间
    //s.resize(100, 'x');//x给不给都可以 resize的size也到了100
    //size_t sz = s.capacity();
    //cout << "making s grow:\n";
    //for (int i = 0; i < 100; ++i)
    //{
    //	s.push_back('c');
    //	if (sz != s.capacity())
    //	{
    //		//1.5倍扩容
    //		sz = s.capacity();
    //		cout << "capacity changed" << sz << '\n';
    //	}
    //}
    string s("hello world");
    s.resize(5);
    s.resize(20, 'x');//前5个是hello 后15个补15个x
    //reverse开空间
    //resize开空间+填数据
    cout << s << endl;
}

5°增删查改

  • push_back 尾插
  • 也可以换成+=
  • insert 插入
  • erase 删除
void test_string6()
{
    //string s;
    //s.push_back('x');
    //s.append("111111");
    推荐+=
    //s += 'x';
    //s += "xxxxxx";
    //cout << s << endl;
    string s;
    s += '1';
    s += "3456";
    cout << s << endl;
    s.insert(s.begin(), '0');//头插 迭代器+字符
    cout << s << endl;
    s.insert(2, "2");//2看成下标
    cout << s << endl;
    s.erase(2, 3);//2的位置开始删除3个字符 如果是10 会一直删完 不会报错
    cout << s << endl;
}

void test_string7()
{
    string s1("hello");
    s1 += ' ';
    s1 += "world";
    cout << s1 << endl;
    //遍历每一个字符
    //1.for
    for (size_t i = 0; i < s1.size(); ++i)
    {
        cout << s1[i] << " ";
    }
    cout << endl;
    //2.迭代器
    string::iterator it1 = s1.begin();
    while (it1 != s1.end())
    {
        cout << *it1 << " ";
        ++it1;
    }
    cout << endl;
    //3.范围for
    for (auto e : s1)
    {
        cout << e << " ";
    }
    cout << endl;
    //4.获取字符数组首地址 用C字符串的形式遍历
    const char* str = s1.c_str();
    while (*str)
    {
        cout << *str << " ";
        ++str;
    }
    cout << endl;

    cout << s1 << endl;//调用string重载的operator<<  将对象数组中的所有字符都输出
    cout << s1.c_str() << endl;//直接输出const char* 遇到\0就结束

    //不一样的地方
    s1 += '\0';
    s1 += "world";
    cout << s1 << endl;
    cout << s1.c_str() << endl;//遇到‘\0’就停了

    char str1[] = "中国";
    cout << str1 << endl;
    str1[3] = -7;
    cout << str1 << endl;
    str1[3] = -8;
    cout << str1 << endl;
    str1[3] = -9;
    cout << str1 << endl;
}

6°取后缀名

void split_url(const string& url)//取别名 利用别名操作 如果想换网址 传参改变即可
{
    //取http
    size_t i1 = url.find(":");
    if (i1 != string::npos)
    {
        //0-i1
        cout << url.substr(0, i1) << endl;
    }
    //取中间部分
    size_t i2 = url.find('/', i1 + 3);//从i1+3开始找/
    if (i2 != string::npos)
    {
        //i2-(i1+3)就是结束位置
        cout << url.substr(i1 + 3, i2 - i1 - 3) << endl;
    }
    //取第三段 就直接从i2+1开始 不用设置结束
    cout << url.substr(i2 + 1) << endl;
}


void test_string8()
{
    //如何取后缀名
    string s1("string.cpp");
    string s2("string.c");
    string s3("string.txt");

    size_t pos1 = s1.find('.');//先拿到点的位置
    if (pos1 != string::npos)//npos是这个字符串的结束位置
    {
        cout << s1.substr(pos1) << endl;//子串打印
    }

    size_t pos2 = s2.find('.');
    if (pos2 != string::npos)
    {
        cout << s2.substr(pos2) << endl;
    }

    size_t pos3 = s3.find('.');
    if (pos3 != string::npos)
    {
        cout << s3.substr(pos3) << endl;
    }
    //怎么拿到.zip
    string s4("string.cpp.zip");
    //rfind倒着找
    size_t pos4 = s4.rfind('.');
    if (pos4 != string::npos)
    {
        cout << s4.substr(pos4) << endl;
    }
    
    //如何取出网址三部分?
    //协议 域名 资源名称
    string cppurl("https://cplusplus.com/reference/");
    string nowcodernul("https://www.nowcoder.com/exam/company");
    //http ip DNS 查ip
    split_url(cppurl);
    split_url(nowcodernul);
}

7°非成员函数

operator+ 尽量少用,因为传值返回,导致深拷贝效率低 operator>> 输入运算符重载 operator<< 输出运算符重载 getline 获取一行字符串 relational operators 大小比较

void test_string9()
{
    string s1("hello");
    string ret1 = s1 + "world";//s1不变
    cout << s1 << endl;
    cout << ret1 << endl;
    string ret2 = s1 += "world";//s1变
    cout << s1 << endl;
    cout << ret2 << endl;
    
    string s2("abcd");
    string s3("bbcd");
    //三种比较
    cout << (s2 < s3) << endl;
    cout << (s2 < "bbcd") << endl;
    cout << ("abcd" < "bbcd") << endl;
}

3.简单string类

面试题:实现一个简单的string类

也就是要实现 构造 析构 赋值 拷贝构造 operator[ ] size等

1°构造

namespace s
{
    class string
    {
    public:
        string(const char* str = "")//不能给空
            :_str(new char[strlen(str)+1])//str错的 //string对象中存储指针 指针指向的数组中存储字符 字符最后必须保留\0
        {
            strcpy(_str, str);//字符串拷贝 拷过来 初始化
        }
    private:
        char* _str;
    };
}
  • 为什么不能直接_str(str)初始化?

假设string s1("hello");

此时s1在栈上 初始化字符串在代码段上 不可以修改 那么增删等功能无法实现 因此要new出空间来保证增删等功能的实现

  • 为什么缺省参数不能给空?

后续的strcpy会有指针的使用 空指针使用会导致程序崩溃

2°析构

namespace s
{
    class string
    {
    public:
        ~string()
        {
            delete[] _str;
        }
    private:
        char* _str;
    };
}

直接使用delete[ ] 清除_str

3°拷贝构造

namespace s
{
    class string
    {
    public:
        string(const string& s)
            :_str(new char[strlen(s._str)+1])//开空间
        {
            strcpy(_str,s._str);//开空间再拷贝
        }
    private:
        char* _str;
    };
}

拷贝构造与构造类似 只是需要一个被拷贝的对象s 通过s.来访问_str

  • 如果不自己实现拷贝构造 而是用编译器的拷贝构造 会有什么问题?

比如说 string s1("hello"); string s2(s1);

s2拷贝s1的hello

看上去没什么问题

但是构造完以后 会自动调用析构函数

编译器默认的拷贝构造是浅拷贝 也就是说两个hello的地址是一样的

此时会将这块空间释放两次 导致程序崩溃

因此解决这个问题 我们必须有自己写一个拷贝构造 完成深拷贝

此时两个hello所处的空间不同 不会造成同一块空间释放两次的问题

为什么同一块空间释放两次就有问题?

第一次释放完之后把这块空间还给了系统

第二次再释放 不可能将系统自带的空间释放 因此程序崩溃

4°赋值

namespace s
{
    class string
    {
    public:
        string& operator=(const string& s)
        {
            if (this != &s)//地址不同 防止自己赋值自己
            {
                char* tmp = new char[strlen(s._str) + 1];//跟s3开一样大的空间
                strcpy(tmp, s._str);//拷贝到新空间
                delete[] _str;//释放s1的旧空间
                _str = tmp;//新空间拿过来
            }
            return *this;
            //出作用域*this不在 加上引用 //返回是为了支持连=
        }
    private:
        char* _str;
    };
}

赋值的过程:

比如说s1=s3

s就看成s3 本来要传两个参数的 但是编译器隐藏了this指针 只用传一个

首先s1要开跟s3一样大的空间

再把s3的数据拷贝到s1的新空间

s1的旧空间释放

s1的指针对象指向新空间(原来是指向旧空间的)

最后返回s1 也就是隐藏的*this

如果不自己实现赋值 那么也会是浅拷贝 与拷贝构造问题一样
 

5°size

namespace s
{
    class string
    {
    public:
        size_t size()
        {
            return strlen(_str);
        }
    private:
        char* _str;
    };
}

6°[]重载

namespace s
{
    class string
    {
    public:
        char& operator[](size_t i)
        {
            return _str[i];
        }
    private:
        char* _str;
    };
}

4.完整string类

简单string类+增删查改等(顺序表)

1°默认成员函数(有现代写法)

namespace szh
{
    class string
    {
    public:
        //string s2("hello")
        string(const char* str = "")
        {
            _size = strlen(str);
            _capacity = _size;
            _str = new char[_capacity + 1];//有一个是\0
            strcpy(_str, str);
        }
        
        //拷贝构造的现代写法
        //string s2(s1)
        string(const string& s)
            :_str(nullptr)
            , _size(0)
            , _capacity(0)
        {
            string tmp(s._str);
            //this->swap(tmp);//s2跟s1换 this和tmp换 this调用成员函数swap
            swap(tmp);
        }

        void swap(string& s)
        {
            ::swap(_str, s._str);
            ::swap(_size, s._size);
            ::swap(_capacity, s._capacity);
            //::swap是库里面的
        }

        //s1 = s3
        string& operator=(string s)
        {
            //this->swap(s);
            swap(s);
            return *this;//s3给到s1 s给到this 交换
        }

        ~string()
        {
            delete[] _str;
            _str = nullptr;
            _size = _capacity = 0;
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

现代写法:

拷贝构造和赋值 是把即将被拷贝和被赋值的字符串与拷贝和赋值的字符串进行交换

_size _capacity与顺序表一致

2°size

namespace szh
{
    class string
    {
    public:
      size_t size() const//可传const对象
        {
            return _size;
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

3°capacity

namespace szh
{
    class string
    {
    public:
        size_t capacity() const//可传const对象
        {
            return _capacity;
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

4°operator[]

namespace szh
{
    class string
    {
    public:
        char& operator[](size_t i)
        {
            assert(i < _size);
            return _str[i];
        }

        char& operator[](size_t i) const//解决const对象访问
        {
            assert(i < _size);
            return _str[i];
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

5°c_str

namespace szh
{
    class string
    {
    public:
        const char* c_str()
        {
            return _str;
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

以c语言的形式输出字符串

6°reverse

增容到n个空间

namespace szh
{
    class string
    {
    public:
        void reserve(size_t n)
        {
            if (n > _capacity)
            {
                char* newstr = new char[n + 1];//多开一个空间给到\0
                strcpy(newstr, _str);//字符串和空间全部保留 释放后指向新空间
                delete[] _str;
                _str = newstr;
                _capacity = n;
            }
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

与赋值函数步骤相同

开n+1个新空间

拷贝字符串

删除旧空间

指向新空间

capacity变为n

7°push_back

namespace szh
{
    class string
    {
    public:
        void push_back(char ch)
        {
            //空间满了则进行增容
            if (_size == _capacity)
            {
                size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
                reserve(newcapacity);
            }
            _str[_size] = ch;
            ++_size;
            _str[_size] = '\0';//添加一个字符时 \0会被替换掉 因此要再加上\0
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

与顺序表的尾插相同 先检查需不需要扩容 再把字符放到尾部 size++ 注意此时\0已经被

替换了 所以最后再补上一个\0

8°append

namespace szh
{
    class string
    {
    public:
        void append(const char* str)
        {
            //空间不够就扩容
            //strcat必须在空间够的情况下
            size_t len = strlen(str);
            //len为追加字符串长度 判断是否需要扩容
            if (_size + len > _capacity)//为什么不能2倍 可能不够
            {
                reserve(_size + len);
            }
            strcpy(_str + _size, str);//直接追加
            _size += len;//插入了len的字符
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

追加字符串

注意:不能使用strcat strcat必须在空间够的情况下

先判断追加后需不需要扩容

再用strcpy进行拷贝 直接从后面位置开始拷贝 相当于就是追加

_size+=len需要调整

9°operator+=

可替代push_back和append进行字符串的拼接

内部调用push_back和append的接口

namespace szh
{
    class string
    {
    public:
    string& operator+=(char ch)
        {
            this->push_back(ch);
            return *this;
        }

        string& operator+=(const char* str)
        {
            this->append(str);
            return *this;
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

返回push_back和append后的字符串 也就是隐藏的*this

10°insert

namespace szh
{
    class string
    {
    public:
      void insert(size_t pos, char ch)
          {
            assert(pos <= _size);//插入在范围之内
            if (_size == _capacity)
            {
                size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
                reserve(newcapacity);
            }
            int end = _size;
            while (end >= (int)pos)
            {
                _str[end + 1] = _str[end];
                end--;
            }
            _str[pos] = ch;
            ++_size;
        }

        string& insert(size_t pos, const char* str)
        {
            assert(pos <= _size);
            size_t len = strlen(str);
            if (_size + len > _capacity)
            {
                int newcapacity = _size + len;
                reserve(newcapacity);
            }
            int end = _size;
            while (end >= (int)pos)//pos等于0的时候 end会转换 -1(无符号)变为最大的数 所以pos要强转一下
            {
                _str[end + len] = _str[end];
                end--;
            }
            //for (size_t i = 0; i < len; ++i)
            //{
            //	_str[pos++] = _str[i];
            //}
            strncpy(_str + pos, str, len);//strcpy会把\0挪过来 直接strncpy 把\0排开
            _size += len;
            return *this; //库里面是要返回自己
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

顺序表的insert 插入后依次挪动数据

注意字符串的插入时的一些细节 pos为0的时候 end会转换 -1的无符号变为最大数

因此要强转 利用strncpy进行直接的追加 可以直接把\0拷贝过来 STL库里面是返回*this的

11°resize

字符初始化 默认给\0 也可以给字符

三类情况

"hello\0" resize(8) 后面三个添字符 resize(18) 需要先扩容 resize(2) 删

namespace szh
{
    class string
    {
    public:
        void resize(size_t n, char ch = '\0')
        {
            //缩 最后位置放\0 n<_size
            if (n < _size)
            {
                _str[n] = '\0';
                _size = n;
            }
            else
            {
                //_size < n <= _capacity
                //扩 n> _capacity
                if (n > _capacity)
                {
                    reserve(n);
                }
                //添的逻辑一样的 只是需要加扩容情况
                for (size_t i = _size; i < n; ++i)//从\0开始添ch
                {
                    _str[i] = ch;
                }
                _size = n;
                _str[_size] = '\0';//添到最后给\0
            }
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

添的时候先检查需不需要扩容

添字符的时候用for循环一个个赋值过来

注意最后需要添\0

12°erase

helloworld\0

如果从w位置开始删 erase(,2) erase(,5) erase(,8) erase(,string::npos)

后三者很轻松 直接删完 pos位置放\0 如果是2的话 就要往前挪数据

namespace szh
{
    class string
    {
    public:
        void erase(size_t pos, size_t len)
        {
            assert(pos < _size);
            if (len >= _size - pos)
            {
                _str[pos] = '\0';
                _size = pos;
            }
            else
            {
                size_t i = pos + len;//r的位置往前挪
                while (i <= _size)
                {
                    _str[i - len] = _str[i];
                    ++i;
                }
                _size -= len;
            }
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

13°find

namespace szh
{
    class string
    {
    public:
        size_t find(char ch, size_t pos = 0)//有多个需要找的 给位置
        {
            for (size_t i = pos; i < _size; ++i)
            {
                if (_str[i] == ch)
                {
                    return i;
                }
            }
        }
        size_t find(const char* str, size_t pos = 0)//查字符串
        {
            char* p = strstr(_str, str);//找子串 在_str中找str
            if (p == nullptr)
            {
                return npos;//没有找到
            }
            else
            {
                return p - _str;//指针相减 就是下标
            }
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

14°operator操作

namespace szh
{
    class string
    {
    public:
        bool operator<(const string& s)
        {
            int ret = strcmp(_str, s._str);
            return ret < 0;
        }
        bool operator==(const string& s)
        {
            int ret = strcmp(_str, s._str);
            return ret == 0;
        }
        bool operator<=(const string& s)
        {
            return *this < s || *this == s;
        }
        bool operator>(const string& s)
        {
            return !(*this <= s);
        }
        bool operator>=(const string& s)
        {
            return !(*this < s);
        }
        bool operator!=(const string& s)
        {
            return !(*this == s);
        }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

15°<<和>>重载

namespace szh
{
    class string
    {
    public:
    //<<重载 方便遍历
    ostream& operator<<(ostream& out, const string& s)
    {
        for (size_t i = 0; i < s.size(); ++i)
        {
            cout << s[i];
        }
        return out;
    }

    //>>重载 这里空格的话就不行了 写一个getline 去掉空格结束条件即可
    istream& operator>>(istream& in, string& s)
    {
        while (1)
        {
            char ch;
            //in >> ch;//>>这个是编译器实现的 换行或者空格才会往下走 导致下面接收不到
            ch = in.get();
            if (ch == ' ' || ch == '\n')//遇到空格或者换行
            {
                break;//输入结束
            }
            else
            {
                s += ch;//调用+=
            }
        }
        return in;
    }
    //getline 遇到换行才停止输入
    istream& getline(istream& in, string& s)
    {
        while (1)
        {
            char ch;
            ch = in.get();
            if (ch == '\n')
            {
                break;
            }
            else
            {
                s += ch;
            }
        }
        return in;
    }
    private:
        char* _str;
        size_t _size;    //已经有多少个有效字符
        size_t _capacity;//能存多少个有效字符 \0不是有效字符
        static size_t npos;
  };
}

16°测试

//构造函数 析构函数 遍历
void test_string1()
{
    string s1;
    string s2("hello");

    cout << s1 << endl;
    cout << s2 << endl;

    cout << s1.c_str() << endl;
    cout << s2.c_str() << endl;

    //三种遍历方式
    //1.for
    for (size_t i = 0; i < s2.size(); ++i)
    {
        s2[i] += 1;
        cout << s2[i] << " ";
    }
    cout << endl;

    //2.迭代器
    string::iterator it2 = s2.begin();
    while (it2 != s2.end())
    {
        *it2 -= 1;
        cout << *it2 << " ";
        ++it2;
    }
    cout << endl;

    //3.范围for 
    //范围for是有迭代器支持的 
    //也就是说这段代码会被最终替换成迭代器
    for (auto e : s2)
    {
        cout << e << " ";
    }
    cout << endl;
}

//插入
void test_string2()
{
    string s1("hello");
    s1.push_back(' ');
    s1.push_back('w');
    s1.push_back('o');
    s1.append("rld xxxxxxxxxxxxxxx");
    cout << s1 << endl;

    //string s2;//空对象会出问题 \0被替换 不够完善
    //s2 += "hello";
    //s2 += ' ';
    //s2 += "world";
    //cout << s2 << endl;
}

//resize
void test_string3()
{
    string s1("hello");
    s1.insert(1, 'x');
    s1.insert(1, "xyz");
    cout << s1 << endl;

    string s2("hello");
    s2.reserve(10);
    cout << s2 << endl;
    cout << s2.size() << endl;
    cout << s2.capacity() << endl;

    s2.resize(8, 'x');
    cout << s2 << endl;
    cout << s2.size() << endl;
    cout << s2.capacity() << endl;

    s2.resize(18, 'a');
    cout << s2 << endl;
    cout << s2.size() << endl;
    cout << s2.capacity() << endl;

    s2.resize(2);
    cout << s2 << endl;
    cout << s2.size() << endl;
    cout << s2.capacity() << endl;
}

//删 查
void test_string4()
{
    string s1("helloworld");
    s1.erase(5, 2);
    cout << s1 << endl;

    s1.erase(5, 4);
    cout << s1 << endl;

    string s2("abcdabcdef");
    cout << s2.find("cde") << endl;
    cout << s2.find("cdex") << endl;
 }

//输入 输出
void test_string5()
{
    string s;
    cin >> s;
    //getline(cin, s); 遇到空格会接着输出
    cout << s;
}

17°copy

namespace copy
{
    class string
    {
    public:
        string(const char* str = " ")
            :_str(new char[strlen(str) + 1])
        {
            strcpy(_str, str);
        }
        ~string()
        {
            delete[] _str;
        }
        //深拷贝 -传统写法
        //string(const string& s)
        //	:_str(new char[strlen(s._str)+1])
        //{
        //	strcpy(_str, s._str);
        //}
        //深拷贝 -现代写法 nullptr换已经搞好的
        string(const string& s)//如果不制空 随机值与tmp交换 tmp出作用域后析构 程序崩溃
            :_str(nullptr)
        {
            string tmp(s._str); //tmp去调构造 开数据 就是s1
            swap(_str, tmp._str);//s2和s1一换 string s2(s1)
        }

        //赋值传统
        //string& operator=(const string& s)
        //{
        //	if (this != &s)
        //	{
        //		char* tmp = new char[strlen(s._str) + 1];
        //		strcpy(tmp, s._str);
        //		delete[] _str;
        //		_str = tmp;
        //	}
        //	return *this;
        //}

        赋值现代
        //string& operator=(const string& s)
        //{
        //	if (this != &s)
        //	{
        //		string tmp(s);
        //		swap(_str, tmp._str);
        //	}
        //	return *this;
        //}

        //更简单的
        string& operator=(string s)//传值
        {
            swap(_str, s._str);
            return *this;
        }

        size_t size()
        {
            return strlen(_str);
        }

        char& operator[](size_t i)
        {
            return _str[i];
        }
    private:
        char* _str;
    };

    void test_string1()
    {
        string s1("hello");
        string s2(s1);
        for (size_t i = 0; i < s2.size(); ++i)
        {
            cout << s2[i];
        }
        cout << endl;
        string s3("world");
        s1 = s3;
        for (size_t i = 0; i < s2.size(); ++i)
        {
            cout << s1[i];
        }
    }
}

【C++】7.string 完

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的小恒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值