模拟实现string类
一、构造相关
构造函数
用初始化列表声明时注意声明顺序
char*类型构造
用strcpy函数实现,strcpy拷贝时会自动拷贝’\0’
string(const char* str = "")
//初始化的顺序只和成员变量声明的顺序有关
:_size(strlen(str))
, _capacity(_size)
, _str(new char[_size + 1])
{
strcpy(_str, str);
}
n个字符构造
调用memset函数实现,结尾加’\0’
string(size_t n, char ch)
{
_size = n;
_capacity = _size;
_str = new char[_size + 1];
memset(_str, ch, n);
_str[n] = '\0';//结尾
}
拷贝构造函数
现代版写法
复用构造函数定义临时变量,使用swap函数对两个对象的_str进行交换,注意这里的swap是函数模板,可以交换任意类型,非string类成员函数。
string(const string& s)
:_str(nullptr)
{
string temp(s._str);
//std::swap(temp, *this);
std::swap(_str, temp._str);
}
传统版写法
申请空间,调用strcpy进行拷贝
string(const string& s)
{
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
}
析构函数
删除空间,置空指针
~string()
{
delete[]_str;
_str = nullptr;
}
赋值运算符重载
现代版写法1
采用传值传参,不用定义临时变量
string& operator=(string s)
{
std::swap(_str, s._str);
return *this;
}
现代版写法2
传引用传参,需判断是否给自己赋值,且要定义临时变量,复用拷贝构造函数
string& operator=(const string& s)
{
if (this != &s)
{
string temp(s);//调用拷贝构造
swap(_str, temp._str);//temp被销毁后自动释放旧空间
}
}
传统版写法
⭐直接让_str指向申请的新空间,旧的空间无法释放;而如果先删除旧空间,new可能会申请失败。所以必须定义临时变量。
string& operator=(const string& s)
{
if (this != &s)
{
char* temp = new char[strlen(s._str) + 1];
strcpy(temp, s._str);
delete _str;
_str = temp;
}
return *this;
}
二、迭代器
定义
将指针当作迭代器使用
typedef char* iterator;
typedef char* reverse_iterator;
正向迭代器
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
三、容量相关
size、lenth
size和lenth含义相同,但lenth为string类独有
size_t size()
{
return _size;
}
size_t lenth()
{
return _size;
}
capacity
size_t capacity()
{
return _capacity;
}
empty
判断当前对象内容是否为空
bool empty()
{
return _size == 0;
}
clear
清空内容,但不影响容量
void clear()
{
_str[_size] = '\0';
_size = 0;
}
resize
改变有效元素个数,如果空间不够要进行扩容,复用append函数,给新增的空间填充内容;如果是减小有效元素个数,将’\0’前移即可。注意改变_size的值
void resize(size_t newsize, char ch = '\0')
{
if (newsize > _size){
if (newsize > _capacity){
reserve(newsize);
}
//扩容的空间用ch填充
append(newsize - _size, ch);
}
else{
_str[newsize] = '\0';
}
_size = newsize;
}
reserve
申请新空间,拷贝元素,删除旧空间。注意改变_capacity的值
void reserve(size_t newcapacity)
{
if (newcapacity > _capacity)
{
char* temp = new char[newcapacity + 1];//+1保存'\0'
strcpy(temp, _str);
delete[] _str;
_str = temp;
_capacity = newcapacity;
}
}
四、元素访问
"[]"符合重载
重载成两种形式,普通成员函数和const成员函数;const成员函数可以访问const成员变量。
char operator[](int n)
{
assert(n < _size);
return _str[n];
}
const char operator[](int n)const
{
assert(n < _size);
return _str[n];
}
五、元素修改
+=
拼接string类对象
复用append
string& operator+=(string& s)
{
append(s);
return *this;
}
拼接单个字符
string& operator+=(char c)
{
append(1, c);
return *this;
}
拼接char*类型字符串
string& operator+=(const char* str)
{
append(str);
return *this;
}
⭐append
追加多个字符
判断追加后的空间若大于_capacity,则需要进行扩容,调用memset函数将空间初始化,注意给空间结尾位置加’\0’
string& append(size_t n, char c)
{
if (_size + n > _capacity){
reserve(_size + n);
}
//_str+_size 要追加的起始位置
//memset(_str, c, n);
memset(_str + _size, c, n);
_size += n;
_str[_size + n] = '\0';
return *this;
}
追加char*类型字符串
调用strcat函数进行字符串拼接
string& append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(len + _size);
}
strcat(_str, str);
_size += len;
return *this;
}
追加string类对象
复用c_string函数和追加char*类型字符串的append
string& append(string& s)
{
append(s.c_string());
return *this;
}
push_back
复用append,追加一个字符
void push_back(char c)
{
append(1, c);
}
insert
任意位置的插入,这里插入的是pos位置之前,将pos及其之后的所有元素向后移一个单位,将要插入的字符c放到pos位置,注意给末尾田间’\0’
string& insert(int pos, char c)
{
if (_size + 1 > _capacity){
resize(_size + 1);
}
//插入在pos之前的位置
for (int i = _size; i > pos; i--){
_str[i] = _str[i - 1];
}
_str[pos] = c;
_str[_size + 1] = '\0';
return *this;
}
erase
npos是形参的默认值,表示size_t类型的最大值,如果len没有给出,则默认从当前位置erase到结尾;循环将pos+len到结尾的元素向前移动len个单位;复用resize函数改变_size大小,并给结尾添加’\0’,返回值为移除后的对象。
string& erase(size_t pos = 0, size_t len = npos)
{
if (len == npos){
len = _size - 1;
}
for (int i = pos; i + len < _size; i++)//循环条件
{
_str[i] = _str[i + len];
}
resize(_size - len);
return *this;
}
swap
使用swap函数模板交换各个成员变量的值
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
六、string类操作
c_string
返回char*类型字符串
const char* c_string()const
{
return _str;
}
find
查找某一个字符,返回其第一次出现的位置,未找到则返回npos
size_t find(char c, size_t pos = 0)
{
for (int i = pos; i < _size; i++){
if (_str[i] == c){
return i;
}
}
return npos;
}
rfind
反向查找字符,返回其第一次出现的位置
size_t rfind(char ch, size_t pos)
{
for (int i = _size - 1; i >= 0; i--){
if (_str[i] == ch){
return i;
}
}
return npos;
}
substr
截取字串,注意截取字串并不是改变原来的字符串。调用strncopy函数从源字符串pos位置拷贝n个字符。
string substr(size_t pos = 0, size_t len = npos)const
{
if (len == npos){
len = _size - pos;
}
char* temp = new char[len + 1];
strncpy(temp, _str + pos, len);
temp[len] = '\0';
string s(temp);
delete[] temp;
return s;
}
七、比较大小
compare
调用strcmp函数
int compare(const string& s)const
{
return(strcmp(_str, s._str));
}
‘>’
bool operator>(const string& s)const
{
if (strcmp(_str, s._str) == 1)
{
return true;
}
return false;
}
‘==’
bool operator==(const string& s)const
{
if (strcmp(_str, s._str) == 0)
{
return true;
}
return false;
}
‘>=’
复用’>‘和’='即可
bool operator>=(const string& s)const
{
//复用
if (*this == s || *this > s)
{
return true;
}
return false;
}
‘<’
复用’>=‘
bool operator<(const string& s)const
{
//复用>
if (*this >= s)
{
return false;
}
return false;
}
‘<=’
复用’>’
bool operator<=(const string& s)const
{
if (*this > s)
{
return false;
}
return true;
}
八、成员变量
static静态成员变量,在类中声明,类外定义
private:
size_t _size;
size_t _capacity;
char* _str;
static size_t npos;//类中声明为静态成员变量,所有对象可以共享
定义npos
size_t string::npos = -1;//类外定义
九、非类成员函数
因为类成员函数的第一个形参都是this无法修改,而’<<‘和’>>'的重载都要求第一个参数是ostream或istream类的对象,否则重载以后类对象就成为了左操作数,不符合常规:
流插入运算符’<<'重载
类外定义,类内声明为友元类,可以访问私有成员,返回值和第一个形参类型都是ostream类对象的引用
定义:
ostream& operator<<(ostream& _cout, const string& s)
{
_cout << s._str;
return _cout;
}
声明:
friend ostream& operator<<(ostream& _cout, const string& s);
流提取运算符’>>'重载
返回值和参数类型为istream类对象的引用
定义:
istream& operator>>(istream& _cin, string& s)
{
_cin >> s._str;
return _cin;
}
声明:
friend istream& operator>>(istream& _cin, string& s);
十、附:完整string类,含测试用例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
#include<string.h>
#include<Windows.h>
using namespace std;
namespace ZH
{
class string
{
public:
//迭代器
typedef char* iterator;
typedef char* reverse_iterator;
// 构造相关
//构造函数
//
string(const char *str= "")
//初始化的顺序只和成员变量声明的顺序有关
:_size (strlen(str))
,_capacity (_size)
, _str(new char[_size + 1])
{
strcpy(_str, str);
}
string(size_t n, char ch)
/*:_size(n)
,_capacity(_size)
,_str(new char[_size+1])*/
{
_size=n;
_capacity = _size;
_str = new char[_size + 1];
memset(_str, ch, n);
_str[n] = '\0';
}
//拷贝构造
string(const string& s)
:_str(nullptr)
{
string temp(s._str);
//std::swap(temp, *this);
std::swap(_str, temp._str);
}
/*string(const string& s)
{
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
}*/
//析构
~string()
{
delete[]_str;
_str = nullptr;
}
//赋值运算符重载
//传统
/*string& operator=(const string& s)
{
if (this != &s)
{
char* temp = new char[strlen(s._str) + 1];
strcpy(temp, s._str);
delete _str;
_str = temp;
}
return *this;
}*/
/*string& operator=(const string& s)
{
if (this != &s)
{
string temp(s);//调用拷贝构造
swap(_str, temp._str);//temp被销毁后自动释放旧空间
}
}*/
string& operator=(string s)
{
std::swap(_str, s._str);
return *this;
}
//iterator
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
//
//capacity
size_t size()
{
return _size;
}
size_t capacity()
{
return _capacity;
}
size_t lenth()
{
return _size;
}
bool empty()
{
return _size == 0;
}
void clear()
{
_str[_size] = '\0';
_size = 0;
}
void resize(size_t newsize,char ch='\0')
{
if (newsize > _size)
{
if (newsize > _capacity)
{
reserve(newsize);
}
//扩容的空间用ch填充
append(newsize - _size, ch);
}
else
{
_str[newsize] = '\0';
}
_size = newsize;
}
void reserve(size_t newcapacity)
{
if (newcapacity > _capacity)
{
char* temp = new char[newcapacity+1];//+1保存'\0'
strcpy(temp, _str);
delete[] _str;
_str = temp;
_capacity = newcapacity;
}
}
/
// access
char operator[](int n)
{
assert(n < _size);
return _str[n];
}
const char operator[](int n)const
{
assert(n < _size);
return _str[n];
}
//
//modifiers
string& operator+=(string& s)
{
append(s);
return *this;
}
string& operator+=(char c)
{
append(1,c);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
string& append(size_t n,char c)
{
if (_size + n > _capacity)
{
reserve(_size + n);
}
//_str+_size 要追加的起始位置
//memset(_str, c, n);
memset(_str + _size, c, n);
_size += n;
_str[_size+n] = '\0';
return *this;
}
string& append(const char*str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(len + _size);
}
strcat(_str, str);
_size += len;
return *this;
}
string& append(string& s)
{
append(s.c_string());
return *this;
}
void push_back(char c)
{
append(1, c);
}
string& insert(int pos, char c)
{
if (_size + 1 > _capacity)
{
resize(_size + 1);
}
//插入在pos之前的位置
for (int i = _size; i > pos; i--)
{
_str[i] = _str[i - 1];
}
_str[pos] = c;
_str[_size + 1] = '\0';
return *this;
}
string& erase(size_t pos = 0, size_t len = npos)
{
if (len == npos) {
len = _size - pos;
}
for (int i = pos; i + len < _size; i++)//循环条件
{
_str[i] = _str[i + len];
}
resize(_size - len);
return *this;
}
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
//
//string operations
const char* c_string()const
{
return _str;
}
size_t find(char c, size_t pos=0)
{
for (int i = pos; i < _size; i++)
{
if (_str[i] == c)
{
return i;
}
}
return npos;
}
size_t rfind(char ch,size_t pos)
{
for (int i = _size - 1; i >= 0; i--)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
string substr(size_t pos = 0, size_t len = npos)const
{
if (len == npos)
{
len = _size - pos;
}
char* temp = new char[len + 1];
strncpy(temp, _str + pos, len);
temp[len] = '\0';
string s(temp);
delete[] temp;
return s;
}
//
//compare
int compare(const string& s)const
{
return(strcmp(_str, s._str));
}
bool operator>(const string& s)const
{
if (strcmp(_str, s._str) == 1)
{
return true;
}
return false;
}
bool operator==(const string& s)const
{
if (strcmp(_str, s._str) == 0)
{
return true;
}
return false;
}
/*bool operator>=(string& s)
{
if (strcmp(_str, s._str) == 1 || strcmp(_str, s._str) == 0)
{
return true;
}
return false;
}*/
bool operator>=(const string& s)const
{
//复用
if (*this==s|| *this>s)
{
return true;
}
return false;
}
bool operator<(const string& s)const
{
//复用>
if (*this >= s)
{
return false;
}
return false;
}
bool operator<=(const string& s)const
{
if (*this > s)
{
return false;
}
return true;
}
//
//成员变量
friend ostream& operator<<(ostream& _cout, const string& s);
friend istream& operator>>(istream& _cin, string& s);
private:
size_t _size;
size_t _capacity;
char* _str;
static size_t npos ;//类中声明为静态成员变量,所有对象可以共享
};
size_t string::npos = -1;//类外定义
//流插入运算符重载,类外定义,类内声明为友元类,可以访问私有成员
ostream& operator<<(ostream& _cout, const string& s)
{
_cout << s._str;
return _cout;
}
//流提取运算符重载,类型为istream
istream& operator>>(istream& _cin, string& s)
{
_cin >> s._str;
return _cin;
}
}
void Test1()
{
ZH::string s1("hello world");
s1.insert(7, 'W');//
}
void Test2()
{
ZH::string s1("1234");
s1.resize(10,'c');
s1.reserve(100);
}
void Test3()
{
ZH::string s1("0123456789");
s1.erase(2, 3);
cout << s1.capacity() << endl;
}
void Test4()
{
ZH::string s1("hello");
ZH::string s2("world");
s1.swap(s2);
}
void Test5()
{
ZH::string s1("hello");
s1 += 'w';
const ZH::string s2("hello");
cout << s2[1];
}
void Test6()
{
ZH::string s1("hello world");
ZH::string s2(s1);
cout << s2 << endl;
string s3;
cin >> s3;
cout << s3 << endl;
}
void Test7()
{
ZH::string s1("world");
ZH::string s2("linux");
ZH::string s3("hello");
bool a=s1.compare(s2);
bool b = s3.compare(s1);
bool c = s3.compare(s3);
cout << a << b << c << endl;
}
void Test8()
{
ZH::string s1("world");
ZH::string s2("linux");
ZH::string s3("hello");
ZH::string s4("hello");
bool a = s1 > s2;
bool b = s3 < s4;;
bool c = s4 == s4;
}
void Test9()
{
ZH::string s("data");
s.reserve(10);
s.insert(1, 'd');
cout << s;
}
int main()
{
Test3();
system("pause");
return 0;
}