目录
示: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); //拷贝构造临时对象tmp
swap(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;
}
}