【C++进阶】STL-string的简单实现
目录
1.构造函数&析构函数
2. 拷贝构造函数与“=”
3.简易版STL-string代码
4. 增删查改版STL-string
resize详解:
erase详解:
完整代码&测试
1.构造函数&析构函数
想要实现下面的代码,我们该如何编写构造函数?
void test_string1()
{string s1("hello");string s2;
}
string本身其实只需要一个字符指针;构造函数使用全缺省的一个写法,我们可以这么写:
string.h
#pragma once
#include<iostream>
namespace luo_string
{class string{private:char* _str;public:// 全缺省构造函数string(const char* str = ""):_str(new char[strlen(str) + 1]){strcpy(_str, str);}~string(){delete[] _str;_str = nullptr;}size_t size(){return strlen(_str);}char& operator[](size_t i) // 返回引用,能修改{return _str[i];}};void test_string1(){string s1("hello");string s2;}}
①首先为了避免命名冲突,我们可以写一个自己的命名空间,所有代码写在这个命名空间之内;
②接下来看构造函数,需要传入一个字符串,也就是const char* 类型的数据,所以需要用此类型作为形参,如果没有传入任何参数,这里就默认给“”,也就是空字符串,这里的空字符串包含一个字符也就是\0,这里给_str分配空间使用strlen对传入的数据计算 + 1,最后使用strcpy拷贝数据;
③析构函数就非常简单,直接将_str的空间释放,_str给一个nullptr,避免野指针;
2. 拷贝构造函数与“=”
如果不实现拷贝构造函数,那么下面的操作会调用系统默认的拷贝构造函数,那么就会出现问题;
void test_string2()
{string s1("hello");string s2(s1);
}
系统默认的构造函数是浅拷贝,也就是说,两个指针指向同一块内存地址;析构函数会调用两次;
具体流程:
s1构造——》s2构造
s2析构——》s1析构
s2析构的时候先将内存释放,再将s2置空;
s1此时仍然指向那个被释放的空间,就会再次对内存空间释放,就会产生报错;

// 拷贝构造函数
string(const string& s) :_str(new char[strlen(s._str) + 1])
{strcpy(_str,s._str);
}
那么赋值运算符“=”,也有这样的特点;
这里重载一下赋值运算符就可以了;这里需要注意内存泄露的问题,例如s1 = s3,这里需要将s1之前的老空间进行释放;

需要给一个额外的空间,将数据传入之后,需要将this指向的老空间释放,再指向新空间;
如果出现自己 = 自己的情况,会将这个流程跳过,直接返回自己;
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;
}
3.简易版STL-string代码
#pragma once
#include<iostream>
namespace luo_string
{class string{private:char* _str;public:// 有参和无参的构造函数// 全缺省构造函数string(const char* str = ""):_str(new char[strlen(str) + 1]){strcpy(_str, str);}// 拷贝构造函数string(const string& s):_str(new char[strlen(s._str) + 1]){strcpy(_str, s._str);}~string(){delete[] _str;_str = nullptr;}size_t size(){return strlen(_str);}char& operator[](size_t i) // 返回引用,能修改{return _str[i];}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;}};void test_string1(){string s1("hello");string s2;}void test_string2(){string s1("hello");string s2(s1);string s3 = s2;}}
4. 增删查改版STL-string
resize详解:
需求:需要将string设置成指定的size大小,使用指定的字符进行填充;
假设:此时的size = 5,capacity = 10;
情况一:将size设置成2,直接将2这个位置填充为'\0',size = 2即可;
情况二:将size设置成8,从size开始到8之前的位置全部替换为ch;,8的位置替换为'\0';
情况三:将size设置成18,先扩容,将capacity变成18,再进行替换操作,同情况二;
void resize(size_t n,char ch = '\0')
{if (n < _size) {_str[n] = '\0';_size = n;}else {if (n > _capacity){reserve(n);}// 写数据for (size_t i = _size; i < n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';}
}
erase详解:
需求:将指定的位置删除指定的长度;
例如:helloword\0,删除pos=5位置,size = 2、5、8个长度;
情况一:从下标5,删除2个元素,其实就是下标为pos + len的元素往前覆盖到pos的位置,被覆盖的下标和覆盖的下标依次++;
情况二、三:删除的len + pos > _size,说明要全部删除完毕;如果不传默认参数为string::npos为整数最大值;此时只需要将pos置为'\0'即可,最后将size变为size - len即可;
void erase(size_t pos, size_t len = npos)
{if (len + pos >= _size) // 说明此处到后面直接删完{_str[pos] = '\0';_size = pos;}else{size_t i = pos + len;// 此处的位置往前挪一位for ( ; i <= _size; i++){_str[i - len] = _str[i];}_size -= len;}
};

完整代码&测试
#pragma once
#include<iostream>
#include<assert.h>
//namespace luo_string
//{
// class string
// {
// private:
// char* _str;
//
//
// public:
// // 全缺省构造函数
// string(const char* str = "")
// :_str(new char[strlen(str) + 1])
// {
// strcpy(_str, str);
// }
//
// // 拷贝构造函数
// string(const string& s)
// :_str(new char[strlen(s._str) + 1])
// {
// strcpy(_str, s._str);
// }
//
// ~string()
// {
// delete[] _str;
// _str = nullptr;
// }
// size_t size()
// {
// return strlen(_str);
// }
// char& operator[](size_t i) // 返回引用,能修改
// {
// return _str[i];
// }
//
// 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;
// }
//
// };
namespace luo_string
{typedef char* iterator;class string{friend std::ostream& operator<<(std::ostream& out, const string& s);friend std::istream& operator>>(std::istream& in, string& s);private:char* _str;size_t _size;size_t _capacity;static size_t npos;public:// 全缺省构造函数string(const char* str = ""){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}// 拷贝构造函数string(const string& s):_str(new char[strlen(s._str) + 1]){strcpy(_str, s._str);_capacity = s._capacity;_size = s._size;}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}size_t size()const{return _size;}size_t capacity()const{return _capacity;}char& operator[](size_t i) // 返回引用,能修改{assert(i < _size);return _str[i];}const char& operator[](size_t i)const // const对象的重载{assert(i < _size);return _str[i];}string& operator=(const string& s){// 防止自己 = 自己if (this != &s){char* tmp = new char[strlen(s._str) + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;}const char* c_str(){return _str;}iterator begin(){return _str;}iterator end(){return _str + _size;}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[]_str;_str = tmp;_capacity = n;}}// 插入一个字符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';}// 插入一个字符串void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}// 重载+=string& operator+=(const char ch){this->push_back(ch);return *this;}string& operator+=(const char* str){this->append(str);return *this;}// 插入单个字符void insert(size_t pos, char ch){assert(pos < _size);if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;reserve(newcapacity);}size_t end = _size;// 从后往前挪while (pos <= end){_str[end + 1] = _str[end]; // ‘\0’放到后面,以此类推--end;}_str[pos] = ch;++_size;}// 插入多个字符void insert(size_t pos, const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size;while (pos <= end){// 每一个值[len]下一个值_str[end + len] = _str[end];--end;}// 赋值/*for (size_t i = 0; i < len; i++){_str[pos+i] = str[i];}*/// 这里不要使用strcpy会将\0一起拷贝strncpy(_str + pos, str, len);_size += len;}// 将size大小变为n/*_size = 5,_capacity = 10情况1:变为8情况2:变为18情况3:变为2*/void resize(size_t n, char ch = '\0'){if (n < _size){_str[n] = '\0';_size = n;}else{if (n > _capacity){reserve(n);}// 写数据for (size_t i = _size; i < n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';}}void erase(size_t pos, size_t len = npos){if (len + pos >= _size) // 说明此处到后面直接删完{_str[pos] = '\0';_size = pos;}else{size_t i = pos + len;// 此处的位置往前挪一位for (; i <= _size; i++){_str[i - len] = _str[i];}_size -= len;}};size_t find(char ch, size_t pos = 0){for (size_t i = pos; i < _size; i++){if (_str[i] == ch) {return i;}}return string::npos;}// 查找子串size_t find(const char* str, size_t pos = 0){// 直接调用c语言库,返回这个位置的指针char* p = strstr(_str, str);if (p == nullptr){return string::npos;}else{return (p - _str);// 返回这个元素的下标}}bool operator<(const string& s){int ret = strcmp(this->_str, s._str);return ret < 0;}bool operator==(const string& s){int ret = strcmp(this->_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 || *this == s;}bool operator!=(const string& s){return !(*this == s);}};size_t string::npos = -1; // -1给size_t就是整数最大值std::ostream& operator<<(std::ostream& out, const string& s){out << s._str;return out;}std::istream& operator>>(std::istream& in, string& s){char ch;//in >> ch; // 拿不到回车while (1){ch = in.get();if (ch == ' ' || ch == '\n'){break;}else {s += ch;}}return in;}void test_string1(){string s1("hello");string s2;}void test_string2(){string s1("hello");string s2(s1);string s3 = s2;luo_string::iterator it = s1.begin();while (it != s1.end()){std::cout << *it << " ";++it;}// 本身没有对范围for做支持,编译器会把范围for直接转换为迭代器for (auto e : s1){std::cout << e;}s1.push_back('a');s1.append("what the fuck\n");// 本身没有对范围for做支持,编译器会把范围for直接转换为迭代器s1 += "hello world\n";s1 += 'y';for (auto e : s1){std::cout << e;}}void test_string3(){string s1("hello");s1.insert(2, "fuck");for (auto e : s1){cout << e;}}void test_string4(){string s1 = "hello";s1.reserve(10); // 容量变成10cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl << endl;s1.resize(8, 'c');cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl << endl;s1.resize(2, 'b');cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl << endl;s1.resize(18, 'a');cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;}void test_string5(){string s1 = "abcdef";cout << s1.find("bcd") << endl;cout << s1.find("bed") << endl;string s2;cin >> s2;cout << s2 << endl;}}
