目录
一. 交换函数swap
二. 默认成员函数
构造函数和析构函数
拷贝构造函数和赋值运算符重载
三. 容量相关操作接口
size 与 capacity
reserve 与 resize
附:reserve与resize的区别
四. 修改相关操作接口
push_pack
append
insert 与 erase
operator+=
find
substr
clear
五. 遍历访问相关接口
使用迭代器
operator[ ]
六. 非成员函数流插入和流提取运算符重载 (<<、>>)
附:完整代码实现
示:string相关的操作接口有很多,这里实现了部分常用接口
一. 交换函数swap
在实现string之前先提供以下三个交换函数,如下:
//用于交换的交换函数模板 template <class T> void swap(T& a, T& b) {T tmp = a;a = b; b = tmp; }
这是一个全局的交换函数,是一个泛型版本。此交换函数被定义在库当中,即头文件为<algorithm>的标准算法库里。但是,如果是交换一般的数据还是可以的;若是想要交换一个存储大量数据的类内容时,因这个交换函数涉及一个复制构造和两个赋值操作,此函数并不是最有效方法。
此时有另外两个交换函数:
这个交换函数是string当中的,是一个非成员函数;其实这是上面通用算法交换的重载,它通过相互转移对其内部数据的所有权来提高其性能(即,字符串交换对其数据的引用,而不实际复制字符)
void swap(string& s) {std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity); }
该交换函数使用行为类似于此成员函数的优化重载 ; 这是为处理大型数据类型提供此函数的重载版本,优化其性能,即只交换几个内部指针而不是它们的全部内容,从而使它们在恒定时间内运行。
说明:以下string的有关拷贝构造或赋值操作将用到以上的交换函数。
二. 默认成员函数
构造函数和析构函数
构造函数:①写两个构造函数(无参和带参);②直接写一个带缺省参数的构造函数(本次采 用这个)
带缺省参数的构造函数:
缺省参数置为空字符串
析构函数:
~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}
拷贝构造函数和赋值运算符重载
拷贝构造:一个已存在的对象去拷贝初始化另一个对象。
首先先拷贝构造一个临时对象,在复用上面开始时所说的交换函数,交换两者指针即可
string(const string& s)
{string tmp(s._str); //拷贝构造临时对象tmpswap(tmp); //交换两者数据,只需要交换两者指针即可
}
赋值运算符重载:将两个已存在的对象进行拷贝——赋值重载
string& operator=(string s) //注意这里不要加引用&
{swap(s);return *this;
}
说明:参数中不加引用的目的就是不改变原对象的中的内容。若加了引用就会改变原对象的内容,也即将s1与s2直接进行交换;这样虽然将s1赋给了s2,但s1本身也改了,这就不符合我们的需求。
三. 容量相关操作接口
size 与 capacity
这是符合字符串内容的实际字节数,不一定等于其存储容量 ;返回字符串的有效数据长度(以字节为单位)。
size_t size() const
{return _size;
}
返回当前为字符串分配的存储容量的大小。此容量不一定等于字符串长度,它可以相等或更大。
size_t capacity() const
{return _capacity;
}
reserve 与 resize
reserve : 如果 n 大于当前字符串容量,则该函数会导致容器将其容量增加到 n 个字符(或更大)
此函数对字符串长度没有影响,并且无法更改其内容;但若 n 小于当前字符串容量,有些平台会缩容(linux),但是不会影像数据;有些不会(vs),取决于编译器,即总的来说,该函数会影响容量,但不会影响有效数据。
void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1]; //开辟新的空间strcpy(tmp, _str); //拷贝原数据到新空间delete[] _str; //释放原空间_str = tmp;_capacity = n; //更新容量}
}
resize:将字符串大小调整为 n 个字符的有效长度。
如果 n 小于当前字符串长度,则当前值将缩短为其前 n 个字符,并删除第 n 个字符以外的字符。
如果 n 大于当前字符串长度,则通过在末尾插入任意数量的字符来扩展当前内容,以达到 n 的大小。如果指定了 c,则新元素将初始化为 c 的副本,否则,它们是值初始化的字符(null字符)。
void string::resize(size_t n, char c)
{//n小于元素的个数if (n < _size){_size = n;_str[_size] = '\0'; //末尾处理\0}else {//n大于当前容量if (n > _capacity){reserve(n); //扩容}for (size_t i = _size; i < n; i++)//将size个元素后的填充为字符c{_str[i] = c;}_str[n] = '\0';_size = n;}
}
附:reserve与resize的区别
reserve用于预留空间,但不会改变容器的大小,只是增加容器的容量,如果超过容器的容量,会重新分配内存。
resize则是直接改变容器的大小,如果改变后的大小比原来的小,会删掉多余的元素,如果比原来的大,会添加默认值的元素。
总之:reserve 改变容器的容量——对应capacity
resize 改变容器的大小——对应size,resize后 都为 有效元素
四. 修改相关操作接口
push_pack
push_back:将字符 c 追加到字符串的末尾,使其长度增加 一。
void push_back(char c)
{if (_size == _capacity) //检查容量{size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = c;_size++;_str[_size] = '\0'; //注意处理末尾\0
}
append
功能:在字符串当前值的末尾追加一个字符串
string& append(const char* str)
{size_t len = strlen(str); //计算追加字符串的长度if (_size + len > _capacity) //检查容量{reserve(_size + len);}strcpy(_str + _size, str); //把要追加的字符串追加到该字符串的末尾_size += len;return *this;
}
insert 与 erase
insert:在pos位置插入一个字符
// 在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char c)
{assert(pos <= _size);if (_size == _capacity) //检查容量{size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}//注意size_t为无符号;这种写法程序会崩/*size_t end = _size;while (end >= pos) {_str[end + 1] = _str[end];--end;}*/size_t end = _size + 1; while (end > pos) //pos位置后元素向后挪{_str[end] = _str[end - 1];--end;}_str[pos] = c;_size++;return *this;
}
insert:在pos位置插入一个字符串
string& insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str); //计算原字符串的长度if (_size + len > _capacity) //检查容量{reserve(_size + len);}size_t end = _size + 1;while (end > pos) //pos后的元素向后挪动(end+len)个长度{_str[end + len - 1] = _str[end - 1];--end;}strncpy(_str + pos, str, len); //注意这里不要用strcpy_size += len;return *this;
}
说明:①挪动数据时从\0 开始挪动 (即 \0 也要挪动)
②这里不使用strcpy 而 使用strncpy, 是因为strcpy会把 \0 也拷贝进去,这样拷贝后有效数据元素的个数(即size)就不一定对了。
erase
功能:删除字符串值中从字符位置 pos 开始并跨越 len 个字符的部分(如果内容太短 或 len 为 nops,则删除直到字符串末尾的部分。
注意:如果不写len(即为默认参数),则会删除pos往后的所有字符(有多少删多少)
// 删除pos位置上的元素,并返回该元素的下一个位置string& erase(size_t pos, size_t len){assert(pos < _size);if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;return *this;}strcpy(_str + pos, _str + pos + len);_size -= len;return *this;}
图解 :
operator+=
功能:在字符串当前值的末尾追加一个字符
string& operator+=(char c)
{push_back(c); //复用push_back即可return *this;
}
功能:在字符串当前值的末尾追加一个字符串
string& operator+=(const char* str)
{append(str); //复用append即可return *this;
}
find
功能:从指定的pos位置处开始(若不指定默认从第一个字符开始)查找一个匹配的字符c
返回值:返回c在字符串中第一次出现的位置
// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos) const
{for (size_t i = pos; i < _size; i++){if (_str[i] == c)return i;}return npos;
}
功能:从指定的pos位置处开始(若不指定默认从第一个字符开始)查找一个匹配的字符串
返回值:第一个匹配项的第一个字符的位置。
如果未找到匹配项,该函数将返回 string::npos。
// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos) const
{const char* ptr = strstr(_str + pos, s);if (ptr == nullptr)return npos;return ptr - _str;
}
substr
返回值:返回一个新构造的对象,其值初始化为此对象的子字符串的副本
string substr(size_t pos, size_t len)
{assert(pos < _size);size_t end = len + pos;if (len == npos || pos + len >= _size){end = _size;}string str;str.reserve(end - pos);for (size_t i = pos; i < end; i++){str += _str[i];}return str;
}
图解:
clear
功能:擦除字符串的内容,该字符串将变为空字符串(长度为 0 个字符)。
void clear()
{_str[0] = '\0'; //注意首字符置为\0_size = 0;
}
五. 遍历访问相关接口
使用迭代器
begin:
功能:如果字符串对象是 const 限定的,则该函数返回const_iterator。否则,它将返回一个迭代器。
//返回iterator迭代器
iterator begin()
{return _str;
}//返回const_iterator迭代器
const_iterator begin() const
{return _str;
}
end:
功能:如果字符串对象是 const 限定的,则该函数返回const_iterator。否则,它将返回一个迭代器。
//返回iterator 迭代器
iterator end()
{return _str + _size;
}//返回const_iterator 迭代器
const_iterator end() const
{return _str + _size;
}
operator[ ]
char& operator[](size_t pos)
{assert(pos <= _size);return _str[pos];
}const char& operator[](size_t pos) const
{assert(pos <= _size);return _str[pos];
}
六. 非成员函数流插入和流提取运算符重载 (<<、>>)
<<: 功能:将符合 str 值的字符序列插入到 os 中。
ostream& operator<<(ostream& out, const string& s)
{for (auto ch : s){out << ch;}return out;
}
>>:
功能:从输入流中提取字符串,将序列存储在 str 中,该序列被覆盖(替换了 str 的先前值)。
注意:输入流提取操作使用空格作为分隔符(在获取字符时会自动忽略空格),因此操作将仅从流中提取可视为单词的内容
istream& operator>>(istream& in, string& s)
{char ch = in.get(); //这里用get()获取字符(包括空格),直到遇到换行结束while (ch != ' ' && ch != '\0'){s += ch;ch = in.get();}return in;
}
附:完整代码实现
#include <iostream>
#include <assert.h>
using namespace std;namespace mystring
{class string{public:typedef char* iterator;typedef const char* const_iterator;public:string(const char* str = "");~string();string(const string& s);string& operator=(const string& s);void resize(size_t n, char c);void resize(size_t n);void reserve(size_t n);void push_back(char c);string& operator+=(char c);string& operator+=(const char* str);void append(const char* str);void clear();void swap(string& s);char& operator[](size_t pos);const char& operator[](size_t pos) const;size_t find(char c, size_t pos = 0) const; size_t find(const char* s, size_t pos = 0) const; string substr(size_t pos = 0, size_t len = npos);string& insert(size_t pos, char c); string& insert(size_t pos, const char* str);string& erase(size_t pos, size_t len = npos); private:char* _str;size_t _size;size_t _capacity;const static size_t npos = -1;};string::string(const char* str){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}string(const string& s){string tmp(s._str);swap(tmp);}string& operator=(string s){swap(s);return *this;}string::~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}// 容量size_t size() const{return _size;}size_t capacity() const{return _capacity;}void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void string::resize(size_t n, char c){if (n < _size){_size = n;_str[_size] = '\0';}else {if (n > _capacity)reserve(n);for (size_t i = _size; i < n; i++){_str[i] = c;}_str[n] = '\0';_size = n;}}void string::resize(size_t n){if (n < _size){_size = n;_str[_size] = '\0';}else{if (n > _capacity)reserve(n);for (size_t i = _size; i < n; i++){_str[i] = '\0';}_size = n;}}// 修改void string::push_back(char c){if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = c;_size++;_str[_size] = '\0';}string& string::operator+=(char c){push_back(c);return *this;}string& string::operator+=(const char* str){append(str);return *this;}void string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}void string::clear(){_str[0] = '\0';_size = 0;}void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// 访问iterator begin(){return _str;}const_iterator begin() const{return _str;}iterator end(){return _str + _size;}const_iterator end() const{return _str + _size;}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];}// 返回c在string中第一次出现的位置size_t string::find(char c, size_t pos) const{for (size_t i = pos; i < _size; i++){if (_str[i] == c)return i;}return npos;}// 返回子串s在string中第一次出现的位置size_t string::find(const char* s, size_t pos) const{const char* ptr = strstr(_str + pos, s);if (ptr == nullptr)return npos;return ptr - _str;}string string::substr(size_t pos, size_t len){assert(pos < _size);size_t end = len + pos;if (len == npos || pos + len >= _size){end = _size;}string str;str.reserve(end - pos);for (size_t i = pos; i < end; i++){str += _str[i];}return str;}// 在pos位置上插入字符c/字符串str,并返回该字符的位置string& string::insert(size_t pos, char c){assert(pos <= _size);if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = c;_size++;return *this;}string& string::insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + 1;while (end > pos){_str[end + len - 1] = _str[end - 1];--end;}strncpy(_str + pos, str, len);_size += len;return *this;}// 删除pos位置上的元素,并返回该元素的下一个位置string& string::erase(size_t pos, size_t len){assert(pos < _size);if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;return *this;}strcpy(_str + pos, _str + pos + len);_size -= len;return *this;}//流插入、流提取ostream& operator<<(ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}istream& operator>>(istream& in, string& s){char ch = in.get();while (ch != ' ' && ch != '\0'){s += ch;ch = in.get();}return in;}}