【C++】7.string

news2024/11/27 0:33:21

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 完

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/402843.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智能网联汽车安全芯片介绍(一)

汽车的新四化(电动化、网联化、智能化、共享化)让汽车安全越来越受到重视,比如一个不太容易被破解的汽车遥控钥匙或者非接触开门等,越智能越开始需要安全。而过去的一些安全事件也凸显了安全的必要性。 黑客早已经盯上了汽车。2015年,Charlie Miller 、 Chris Valsek曾通过…

熟悉mmdetection3d数据在模型中的处理流程

目录1、搭建模型2、读取数据3、运行流程3.1 图像特征获取3.2 点云特征获取3.3 head3.4 编码bbox4、可视化5、总结本图文数据集采取KITTI数据集配置文件的介绍可以参考博主上一篇图文本图文旨在利用一条数据&#xff0c;走完整个多模态数据处理分支&#xff0c;获得bbox&#xf…

Linux内核里的传输层数据流

传输层发送数据包socket sendto系统调用应用程序通过socket调用sendto系统调用发送数据包&#xff0c;根据网络层协议调用inet_sendmsg或者inet6_sendmsg()函数&#xff0c;将数据包送入协议栈发送。SYSCALL_DEFINE6(sendto...) - net/socket.csock_sendmsg() - net/socket.cso…

compose系列教程-2. 显示图片

要在Android中使用Compose显示图片&#xff0c;需要使用Image组件。以下是一个简单的例子&#xff0c;它显示了一张图片&#xff1a; Composable fun MyApp() { val image painterResource(id R.drawable.my_image) Image(painter image, contentDescription "…

dynamics 365的增删改查

今天需要完成对dynamics 365的增删改查&#xff0c;网上一直找不到合适的教程&#xff0c;官方文档看不懂&#xff0c;实在没办法了&#xff0c;于是下载了chatgpt,对他进行了提问。 我&#xff1a;怎么用visual studio基于dynamics 365进行增删改查&#xff1f; ChatGPT 中文…

Python笔记 -- 类

文章目录1、引入2、操作属性3、继承4、将实例用作属性5、导入类1、引入 类和实例 使用类可以模拟任何东西&#xff0c;下面是一个小狗的简单类Dog&#xff0c;它表示任意小狗&#xff0c;实例my_dog表示一个具体的小狗方法 类中的函数称为方法&#xff0c;有关函数的一切均适用…

兔c同学的一篇:使用python 的 unittest模块对类和函数进行测试

文章目录1. 测试函数简单的函数测试单元测试和测试用例可通过的测试不可通过的测试测试未通过时怎么办2. 测试类各种断言方法测试一个类测试 AnonymousSurvey方法setUp()导言 在编写函数或类时&#xff0c;还可为其编写测试。通过测试&#xff0c;可以确定代码面对各种输入都能…

面试官必问--谈谈Spring Bean对象的生命周期吧

现在是时候讨论Spring Bean从产生到销毁整个过程的细节了&#xff0c;也就是Spring Bean的生命周期。在这里文哥先温馨提示&#xff1a;Spring Bean的生命周期是面试高频点之一&#xff0c;希望大家好好掌握哦~一. Spring Bean生命周期的概述如果没有Spring的环境&#xff0c;J…

张力控制之开环模式

张力控制的相关知识也可以参看专栏的其它文章,链接如下: 张力闭环控制之传感器篇(精密调节气阀应用)_RXXW_Dor的博客-CSDN博客跳舞轮对应张力调节范围,我们可以通过改变气缸的气压方式间接改变,张力跳舞轮在收放卷闭环控制上的详细应用,可以参看下面的文章链接,这里我…

人工智能实验一:利用遗传算法求解 TSP(旅行商)问题

1.任务描述 本关任务&#xff1a;利用遗传算法求解 TSP 问题。 2.相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1. 遗传算法&#xff1b;2. TSP问题。 遗传算法 一个后继状态由两个父状态决定&#xff0c;以k个随机产生的状态开始&#xff08;population&…

Kaggle赛题解析:Diffusion Prompt生成

文章目录一、比赛信息二、比赛背景三、比赛任务四、评价指标五、数据描述六、解题思路一、比赛信息 比赛名称&#xff1a;Stable Diffusion - Image to Prompts 推断生成高度详细、清晰的焦点、插图、宏伟、史诗般的 3d 渲染图像的prompt 比赛链接&#xff1a;https://www.k…

python----获取一部小说

1、需求说明 获取一部小说的标题内容&#xff0c;以txt文档形式保存 2、项目说明 3、代码 # 怎么发送请求 # pip install requests import requests# pip install lxml->从标签里提起文字 #from lxml import etree from lxml import html etreehtml.etree # 发送给谁 url…

Android---系统启动流程

目录 Android 系统启动流程 init 进程分析 init.rc 解析 Zygote 概叙 Zygote 触发过程 Zygote 启动过程 什么时Runtime&#xff1f; System Server 启动流程 Fork 函数 总结 面试题 Android 是 google 公司开发的一款基于 Linux 的开源操作系统。 Android 系统启动…

Web3中文|一波未平一波又起:Silvergate将走向何处

Silvergate Capital&#xff08;SI&#xff09;这一加密公司曾经的重要银行合作伙伴&#xff0c;现在正处于崩溃的边缘。这家总部位于加州拉荷亚的公司上周五晚上表示&#xff0c;其暂停了Silvergate交易所网络&#xff08;SEN&#xff1a;Silvergate Exchange Network&#xf…

Foxit PDF SDK ActiveX 5.9.7 Crack

Foxit PDF SDK ActiveX对于刚接触PDF或不愿投入过多精力学习PDF技术的产品管理者及开发者来说&#xff0c;Foxit PDF SDK ActiveX无疑是理想的选择。破解版它拥有操作简单的特性&#xff0c;提供可支持定制的可视化编程组件&#xff0c;开发者通过简单的拖放动作&#xff0c;就…

扬帆配资|建筑业景气度持续回升,多只概念股业绩有望增长

新式城镇化概念股遭到商场重视。 今天早盘&#xff0c;新式城镇化概念股冲高&#xff0c;恒锋信息、ST花王涨停。蕾奥规划、筑博规划一度冲高至15%&#xff0c;冠龙节能、杭州园林、美晨生态跟涨。 国家出台一系列城镇化相关方针 城镇化&#xff0c;是人口向城镇会集的进程。…

【Kubernetes】第二十三篇 - 布署 nodejs 后端项目(上)

一&#xff0c;前言 上一篇&#xff0c;介绍了 MySQL 服务的部署&#xff1b; 本篇&#xff0c;介绍 nodejs 后端项目的布署&#xff08;将后端项目构建成为 docker 镜像&#xff0c;并推送至镜像仓库&#xff09;&#xff1b; 二&#xff0c;准备项目 创建后端项目&#xf…

8、LSM树

一、前言 最近在调研NoSQL数据库&#xff0c;发现RocksDB、LevelDB、HBase以及Prometheus等&#xff0c;其底层的存储引擎都是基于LSM树&#xff0c;于是决定花时间彻底吃透LSM树这一数据结构。 不幸的是&#xff0c;在查阅资料学习的过程中&#xff0c;发现网上各种文章汗牛…

浅谈对Promise的理解以及在工作中的应用

浅谈对Promise的理解以及在工作中的应用Promise的概念背景知识JavaScript的同步和异步JavaScript事件循环回调函数进行异步操作解决方案&#xff1a;PromisePromise 在工作中的运用创建PromisePromise封装AJAXPromise链式操作Promise.all()Promise.race()async和await总结Promi…

轻松转换文档:antennahouse/Office Server Document Converter

关于 Office Server 文档转换器 (OSDC)破解版 无需 Microsoft Office 或 Adob​​e 软件即可快速准确地转换文档。 Office Server 文档转换器 (OSDC) 会将您在 Microsoft Office&#xff08;Word、Excel、PowerPoint&#xff09;中创建的重要文档转换为高质量的 PDF 或图像格式…