网站的中文域名是什么百度官网下载电脑版
目录
一:string类
二:构造函数、拷贝构造函数及析构函数
1、构造函数
2、拷贝构造函数
3、析构函数
三、实现string中对容量操作的成员函数
1、size
2、capacity
3、reserve
4、resize
5、clear
6、empty
四、string类中对象的增删查改操作
1、push_back
2、append
3、c_str
4、find
5、substr
6、insert
7、erase
五、string中重要的运算符重载
1、赋值运算符的重载
2、流插入 <<
3、流提取 >>
4、下标访问 []
5、加等一个字符 +=
6、加等字符串 +=
7、大于 >
8、等于 ==
9、小于 <
10、大于等于 >=
11、小于等于 <=
12、不等于 !=
六、迭代器
最后完整代码
string.h
string.c
test.c
一:string类
首先先定义一个string类
class string
{
public:private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;
};
string的底层是一个个字符,所以定义一个_str 记录字符,_size用来记录这个字符串的长度,_capacity用来记录开了多少空间。
二:构造函数、拷贝构造函数及析构函数
1、构造函数
string的构造函数分为无参构造和有参构造,通过无参构造的对象会默认生成一个空字符串,因此我们可以带一个缺省值,在没有参数传递时就直接构造一空字符串
string(const char* str = ""):_str(new char[strlen(str) + 1]), _size(strlen(str)), _capacity(strlen(str))
{strcpy(_str, str); //将str中的内容拷贝给_str
}
在开空间时,我们需要多开一个空间,因为strlen算出的大小不包括 '\0' ,因此我们需要给 '\0' 留一个空间。
2、拷贝构造函数
拷贝构造函数是默认成员函数,如果不写编译器会自动生成,对于内置类型完成浅拷贝,对于自定义类型调用其构造函数完成拷贝。对于string来说,如果不自己写拷贝构造函数会导致浅拷贝问题。
浅拷贝会使得两个对象指向同一块空间,两个对象在析构时都会调用自己的析构函数,这样同一块空间就会被析构两次;浅拷贝一个对象的数据改变,另一个对象的数据也改变,因为它们指向同一块空间地址。 一个对象被删除后,另一个对象无效
//浅拷贝实例
class ShallowCopyExample {
public:int* data;ShallowCopyExample(int value) {data = new int(value);}// 默认的拷贝构造函数是浅拷贝ShallowCopyExample(const ShallowCopyExample& other) = default;~ShallowCopyExample() {delete data;}
};int main() {ShallowCopyExample obj1(10);ShallowCopyExample obj2 = obj1; // 浅拷贝// 问题:obj1和obj2的data指针指向同一内存// 当其中一个对象析构后,另一个对象的指针就悬空了
}
拷贝构造函数
string(const string& str):_str(new char[str._capacity+1]),_size(str._size),_capacity(str._capacity)
{strcpy(_str, str._str);
}
上面的这种写法是比较常见的一种写法,但是我们还有一种更加简便的写法。我们可以通过已经实现了的构造函数传一个常量字符串,即下面的str._str,来创建一个临时对象,然后将这个临时对象的成员与自己交换,这样也完成了拷贝构造。
void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}
string(const string& str):_str(nullptr), _size(0), _capacity(0)
{//创建一个临时对象string tmp(str._str);swap(tmp);
}
但是,为了写成这个拷贝构造函数我们还写了一个swap函数,这这么就简便了呢?
那是因为通过查阅标准库我们发现swap函数也是一个string类中提供了的函数,因此我们不仅简便了拷贝构造函数的写法,还又完成了一个函数的实现。并且,因为tmp是一个局部对象,因此在出作用域后就会自动调用析构函数,所以交换后还可以清理掉原来的空间,一举两得。
3、析构函数
我们可以使用delete直接释放掉_str的空间
~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}
三、实现string中对容量操作的成员函数
1、size
size返回字符串有效字符的长度。我们可以直接返回其成员变量中的_szie。
size_t size() const
{return _size;
}
2、capacity
capacity返回空间总大小。我们可以直接返回其成员变量中的_capacity。
size_t capacity() const
{return _capacity;
}
3、reserve
reserve为字符串预留空间
void reserve(size_t n)
{if (n > _capacity){//创建一个临时变量,开n + 1个空间char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str; //将原空间删除_str = tmp; // 将临时变量的空间给str_capacity = n; // 预留多少字符给_capacity}
}
4、resize
resize 的功能是将有效字符的个数改成 n 个,多出的空间用字符 ch 填充。
_size < n < _capacity 先预留n大小的空间,直接用字符 填充 n - size 之间的位置,记住要在最后加上'\0'。
_size > n 直接将 n 的位置置换成'\0'。
n > _capacity 先预留 n 大小的空间,剩下的空间用字符 ch 填充,记住要在最后加上'\0'。
void resize(size_t n, char ch)
{if (n > _size){//先检查要不要扩容reserve(n);for (size_t i = _size; i < n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';}else{_str[_size] = '\0';_size = n;}
}
5、clear
作用是清空有效字符
void clear()
{_str[0] = '\0';_size = 0;
}
6、empty
empty的作用是检测字符串释放为空字符串,是返回true,否则返回false。
bool empty() const
{return _size == 0;
}
四、string类中对象的增删查改操作
1、push_back
在字符串后面插字符c
void string::push_back(char ch)
{if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';
}
2、append
在字符串后面追加一个字符串
void string::append(const char* str)
{//先计算追加字符的个数size_t len = strlen(str);//如果 len + _size > _capacity 就需要扩容if (_capacity < len + _size){//重新申请预留空间reserve(len + _size);}//从_str + _size出位置开始复制strcpy(_str + _size, str);_size += len;
}
3、c_str
返回一个C风格的字符串
char* string::c_str() const
{return _str;
}
C风格字符串和string类字符串的区别
void test2()
{string s1("hello");s1 += '\0';s1 += "dafdsaf";cout << s1 << endl;cout << s1.c_str() << endl;
}
上述代码的执行结果是:
C风格字符串以 \0 结尾, 但是string类字符串不一定以 \0 结尾,依赖size()/lenth()。
4、find
在字符串中寻找一个字符,返回第一次出现的下标
npos是string类的静态成员变量,静态成员变量要在类外定义的。我们一般将它设为公共权限,并赋值为-1。
size_t string::find(const char sub, size_t pos) const
{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == sub)return i;}return npos;
}
在字符串中找字符串
size_t string::find(const char* sub, size_t pos) const
{assert(pos < _size);const char* ptr = strstr(_str + pos, sub);if (ptr == nullptr){return npos;}else{//指针-指针就是指针之间的距离 return ptr - _str;}
}
5、substr
它的作用是在str中从pos位置开始,截取n个字符,然后将其返回。如果不传len,则默认截取从pos开始到结尾的全部字符。 如果 pos + n > _size, 说明要截取的字符大于str的长度,则截取从pos开始到结尾的全部字符。
string substr(size_t pos = 0, size_t len = npos) const;string string::substr(size_t pos, size_t len) const
{assert(pos < _size);size_t reallen = len;//如果从pos开始截取n个字符大于_size的长度,则截取这之间的全部字符if (reallen == npos || reallen + pos > _size){reallen = _size - pos;}string sub;for (size_t i = pos; i < reallen + pos; i++){sub += _str[i];}return sub;
}
6、insert
在指定位置前插入一个字符
string& insert(size_t pos, char ch);string& string::insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = size() + 1;while (end > pos){//把pos后面的字符往后挪_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;return *this;
}
在指定位置前插入一个字符串
string& string::insert(size_t pos, const char* ch)
{assert(pos <= _size);//计算要插入的长度size_t len = strlen(ch);//如果要插入的长度加上原本的长度大于_capacity就需要扩容if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (pos + len <= end){_str[end] = _str[end - len];end--;}//从pos位置开始复制要插入长度个字符strncpy(_str + pos, ch, len);_size += len;return *this;
}
7、erase
从pos开始删除n个字符
void erase(size_t pos, size_t len = npos);void string::erase(size_t pos, size_t len)
{assert(pos < _size);if (len == npos || len >= _size){_str[pos] = '\0';_size = pos;}else{//从pos位置开始复制strcpy(_str + pos, _str + pos + len);//_size减了,对应_str字符数也少了_size -= len;}
}
五、string中重要的运算符重载
1、赋值运算符的重载
编译器默认生成的赋值重载也会导致浅拷贝,所以我们需要实现深拷贝。
string& string::operator==(const string& str)
{//如果两个不是指向同一块空间if (this != &str){//创建一个临时数组char* tmp = new char[str._capacity + 1];strcpy(tmp, str._str);delete[] _str;_str = tmp;_size = str._size;_capacity = str._capacity;}return *this;
}
和拷贝构造函数一样,我们也可以用简便写法来实现赋值运算符的重载
string& string::operator=(const string& str)
{if (this != &str){string tmp(str);swap(tmp);}return *this;
}
2、流插入 <<
流插入和流提取都要在类外定义
ostream& operator<<(ostream& out, const string& s)
{for (size_t i = 0; i < s.size(); i++){out << s[i];}return out;
}
3、流提取 >>
istream& operator>>(istream& in, string& s){s.clear();char ch;ch = in.get();const size_t N = 32;char buff[N];size_t i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}buff[i] = '\0';s += buff;}
4、下标访问 []
普通对象:可读可写
char& string::operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}
const对象:可读不可写
char& string::operator[](size_t pos) const{assert(pos < _size);return _str[pos];}
5、加等一个字符 +=
我们可以直接复用push_back来实现一个字符的加等
string& string::operator+=(char str)
{push_back(str);return *this;
}
6、加等字符串 +=
我们可以直接复用append来实现字符串的加等
string& string::operator+=(const char* str){append(str);return *this;}
7、大于 >
bool string::operator>(const string& s) const
{return strcmp(_str, s._str) > 0;
}
strcmp通过比较两个字符串,如果s1 > s2,返回大于1的值;如果s1 < s2,返回小于1的值;如果s1 == s2,返回0;
8、等于 ==
bool string::operator==(const string& s) const
{return strcmp(_str, s._str) == 0;
}
9、小于 <
bool string:: operator<(const string& s) const
{return !(_str > s._str) && !(_str == s._str);
}
10、大于等于 >=
bool string::operator>=(const string& s) const
{return !(_str < s._str);
}
11、小于等于 <=
bool string::operator<=(const string& s) const
{return !(_str > s._str);
}
12、不等于 !=
bool string::operator!=(const string& s) const
{return !(_str == s._str);
}
六、迭代器
迭代器作为STL的六大组件之一,它的作用十分重要。而在string中迭代器的本质就是一个char*或const char*的指针。
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{return _str;
}
const_iterator begin() const
{return _str;
}
iterator end()
{return _str + _size;
}
const_iterator end() const
{return _str + _size;
}
最后完整代码
string.h
#pragma once#include <iostream>
#include <assert.h>
//#include <string>
#include <string.h>
using namespace std;namespace meng
{class string{public:typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}const_iterator begin() const{return _str;}iterator end(){return _str + _size;}const_iterator end() const{return _str + _size;}string& operator+=(char str);string& operator+=(const char* str);string& operator=(const string& str);char& operator[](size_t pos);char& operator[](size_t pos) const;bool operator>(const string& s) const;bool operator<(const string& s) const;bool operator==(const string& s) const;bool operator>=(const string& s) const;bool operator<=(const string& s) const;bool operator!=(const string& s) const;////string(const char* str = ""):_str(new char[strlen(str) + 1]), _size(strlen(str)), _capacity(strlen(str)){strcpy(_str, str); //将str中的内容拷贝给_str}void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}string(const string& str):_str(nullptr), _size(0), _capacity(0){//创建一个临时对象string tmp(str._str);swap(tmp);}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}size_t size() const{return _size;}size_t capacity() const{return _capacity;}void reserve(size_t n);void resize(size_t n, char ch);void clear(){_str[0] = '\0';_size = 0;}bool empty() const{return _size == 0;}void push_back(char ch);void append(const char* str);char* c_str() const;//找一个字符串size_t find(const char* sub, size_t pos = 0) const;size_t find(const char sub, size_t pos = 0) const;string substr(size_t pos = 0, size_t len = npos) const;//insert//在指定位置前插入一个字符string& insert(size_t pos, char ch);//插入一个字符串string& insert(size_t pos, const char* ch);void erase(size_t pos, size_t len = npos);private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;static const size_t npos;};ostream& operator<<(ostream& out, const string& s);istream& operator>>(istream& in, string& s);
}
string.c
#define _CRT_SECURE_NO_WARNINGS#include "string.h"namespace meng
{const size_t string::npos = -1;//运算符的重载string& string::operator+=(char str){push_back(str);return *this;}string& string::operator+=(const char* str){append(str);return *this;}//string& string::operator=(const string& str)//{// //如果两个不是指向同一块空间// if (this != &str)// {// //创建一个临时数组// char* tmp = new char[str._capacity + 1];// strcpy(tmp, str._str);// delete[] _str;// _str = tmp;// _size = str._size;// _capacity = str._capacity;// }// return *this;//}string& string::operator=(const string& str){if (this != &str){string tmp(str);swap(tmp);}return *this;}char& string::operator[](size_t pos){assert(pos < _size);return _str[pos];}char& string::operator[](size_t pos) const{assert(pos < _size);return _str[pos];}bool string::operator>(const string& s) const{return strcmp(_str, s._str) > 0;}bool string:: operator<(const string& s) const{return !(_str > s._str) && !(_str == s._str);}bool string::operator==(const string& s) const{return strcmp(_str, s._str) == 0;}bool string::operator>=(const string& s) const{return !(_str < s._str);}bool string::operator<=(const string& s) const{return !(_str > s._str);}bool string::operator!=(const string& s) const{return !(_str == s._str);}ostream& operator<<(ostream& out, const string& s){for (size_t i = 0; i < s.size(); i++){out << s[i];}return out;}istream& operator>>(istream& in, string& s){s.clear();char ch;ch = in.get();const size_t N = 32;char buff[N];size_t i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}buff[i] = '\0';s += buff;}////void string::reserve(size_t n){if (n > _capacity){//创建一个临时变量,开n + 1个空间char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str; //将原空间删除_str = tmp; // 将临时变量的空间给str_capacity = n; // 预留多少字符给_capacity}}void string::resize(size_t n, char ch){if (n > _size){//先检查要不要扩容reserve(n);for (size_t i = _size; i < n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';}else{_str[_size] = '\0';_size = n;}}void string::push_back(char ch){if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';}void string::append(const char* str){//先计算追加字符的个数size_t len = strlen(str);//如果 len + _size > _capacity 就需要扩容if (_capacity < len + _size){//重新申请预留空间reserve(len + _size);}//从_str + _size出位置开始复制strcpy(_str + _size, str);_size += len;}char* string::c_str() const{return _str;}size_t string::find(const char sub, size_t pos) const{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == sub)return i;}return npos;}size_t string::find(const char* sub, size_t pos) const{assert(pos < _size);const char* ptr = strstr(_str + pos, sub);if (ptr == nullptr){return npos;}else{//指针-指针就是指针之间的距离 return ptr - _str;}}string string::substr(size_t pos, size_t len) const{assert(pos < _size);size_t reallen = len;//如果从pos开始截取n个字符大于_size的长度,则截取这之间的全部字符if (reallen == npos || reallen + pos > _size){reallen = _size - pos;}string sub;for (size_t i = pos; i < reallen + pos; i++){sub += _str[i];}return sub;}string& string::insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = size() + 1;while (end > pos){//把pos后面的字符往后挪_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;return *this;}string& string::insert(size_t pos, const char* ch){assert(pos <= _size);//计算要插入的长度size_t len = strlen(ch);//如果要插入的长度加上原本的长度大于_capacity就需要扩容if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (pos + len <= end){_str[end] = _str[end - len];end--;}//从pos位置开始复制要插入长度个字符strncpy(_str + pos, ch, len);_size += len;return *this;}void string::erase(size_t pos, size_t len){assert(pos < _size);if (len == npos || len >= _size){_str[pos] = '\0';_size = pos;}else{//从pos位置开始复制strcpy(_str + pos, _str + pos + len);//_size减了,对应_str字符数也少了_size -= len;}}}
test.c
#define _CRT_SECURE_NO_WARNINGS#include "string.h"void test()
{string str("hello world");for (auto e : str)cout << e << ' ';cout << endl;//string::iterator it = str.begin()auto it = str.begin();while (it != str.end()){cout << *it << ' ';++it;}cout << endl;for (auto e : str){e += 2;cout << e << ' ';}cout << endl;cout << str << endl;//string::reverse_iterator a = str.rbegin();auto a = str.rbegin();while (a != str.rend()){cout << *a << ' ';++a;}cout << endl;
}namespace meng
{void test1(){string s1("hello world");cout << s1.size() << " " << s1.capacity() << endl;s1.reserve(16);cout << s1.capacity() << endl;//s1.resize(20, 'h');//cout << s1.c_str() << endl;//cout << s1.size() << " " << s1.capacity() << endl;//cout << s1.empty() << endl;s1.push_back('h');cout << s1.c_str() << endl;cout << s1.size() << " " << s1.capacity() << endl;s1.append("xxxxx");cout << s1.size() << " " << s1.capacity() << endl;cout << s1.c_str() << endl;cout << s1.find("xxx", 0) << endl;cout << s1.find('o', 0) << endl;string s2 = s1.substr(4, 5);cout << s2.c_str() << endl;string s3 = s1.insert(3, 'a');cout << s3.c_str() << endl;string s4 = s1.insert(3, "aaaaaa");cout << s4.c_str() << endl;s4.erase(3, 6);cout << s4.c_str() << endl;string s5 = s1; //拷贝构造cout << s5.c_str() << endl;cout << s1 << endl;cout << s4 << endl;cout << (s4 == s1) << endl;cout << (s4 != s1) << endl;cout << (s4 > s1) << endl;cout << (s4 < s1) << endl;}}void test2()
{string s1("hello");s1 += '\0';s1 += "dafdsaf";cout << s1 << endl;cout << s1.c_str() << endl;
}int main()
{//test();meng::test1();//test2();return 0;
}