赞
踩
namespace nzb { class string { public: typedef char* iterator; typedef const char* const_iterator; //默认成员函数 string(const char* str = ""); //构造函数 string(const string& s); //拷贝构造函数 string& operator=(const string& s); //赋值运算符重载函数 ~string(); //析构函数 //迭代器相关函数 iterator begin(); iterator end(); const_iterator begin()const; const_iterator end()const; //容量和大小相关函数 size_t size(); size_t capacity(); void reserve(size_t n); void resize(size_t n, char ch = '\0'); bool empty()const; //修改字符串相关函数 void push_back(char ch); // 尾插字符 void append(const char* str); // 尾插字符串 string& operator+=(char ch); string& operator+=(const char* str); string& insert(size_t pos, char ch); // 在指定位置插入 string& insert(size_t pos, const char* str); string& erase(size_t pos, size_t len); // 删除指定位置 void clear(); void swap(string& s); const char* c_str()const; //访问字符串相关函数 char& operator[](size_t i); const char& operator[](size_t i)const; size_t find(char ch, size_t pos = 0)const; // 查找 size_t find(const char* str, size_t pos = 0)const; private: char* _str; //存储字符串 size_t _size; //记录字符串当前的有效长度 size_t _capacity; //记录字符串当前的容量 static const size_t npos; //静态成员变量(整型最大值) }; const size_t string::npos = -1; //关系运算符重载函数 bool operator>(const string& s1, const string& s2); bool operator==(const string& s1, const string& s2); inline bool operator!=(const string& s1, const string& s2); inline bool operator>=(const string& s1, const string& s2); inline bool operator<(const string& s1, const string& s2); inline bool operator<=(const string& s1, const string& s2); //<<和>>运算符重载函数 istream& operator>>(istream& in, string& s); ostream& operator<<(ostream& out, const string& s); istream& getline(istream& in, string& s); }
注意:为了防止和标准库中的string类产生命名冲突,建议在自己的命名空间中模拟实现
string(char* str = "")
:_size(strlen(str))//记录字符串长度
, _capacity(_size)//设定容量
{
_str = new char[_capacity + 1];
//为存储字符串开辟空间(多开一个用于存放'\0')
strcpy(_str, str);//将字符串拷贝到已开辟好的空间
}
模拟实现拷贝构造函数前,我们应该首先了解深浅拷贝
浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源。当其调用析构函数时会报错,原因如下
当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以 当继续对资源进项操作时,就会发生发生了访问违规。
深拷贝:深拷贝是指源对象与拷贝对象互相独立。其中任何一个对象的改动不会对另外一个对象造成影响。
传统写法
传统写法思路较为简单,就是新建一个string类,再将原string类拷贝过去即可
string(const string& s)
:_str(new char[strlen(s._str) + 1])
, _size(0)
, _capacity(0)
{
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
现代写法
现代写法较为巧妙,调用构造函数新建一个string类,再交换原string类和新string类,出作用域时,新建的string类被销毁,原string类完成拷贝构造
string(const string& s)
:_str(nullptr)
{
string tmp(s._str);
swap(tmp);
}
赋值运算符重载函数和拷贝构造函数类似,分为传统和现代写法,思路大致相同
传统写法
赋值运算符重载函数的传统写法和拷贝构造函数传统写法相比多了销毁原先的空间和检查是否自己给自己赋值
string& operator=(const string& s)
{
if (this != &s) //防止自己给自己赋值
{
delete[] _str; //将原来_str指向的空间释放
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str); //将s._str拷贝一份到_str
_size = s._size;
_capacity = s._capacity;
}
return *this; //返回左值
}
现代写法
通过采用“值传递”接收右值的方法,让编译器自动调用拷贝构造函数,然后我们再将拷贝出来的对象与左值进行交换
string& operator=(string s)
{
swap(s);
return *this;
}
可以和传统写法一样添加判断是否自己给自己赋值,但是现实中很少出现这种情况,不添加判断也可以。
因为在堆上开辟了空间,所以析构函数需要自己写,避免开辟的空间无法释放,造成内存泄漏
~string()
{
delete[] _str; // 释放开辟的空间
_str = nullptr; // 指针赋空
}
string类中的迭代器实际上就是字符指针,只是给字符指针起了一个别名叫iterator而已。
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; }
size记录字符的数量,capacity记录容量大小
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity;
}
实现reserve和resize时一定要注意两者的区别
reserve:
void reserve(size_t n)
{
if (n > _capacity)// 当n大于对象当前的capacity时才执行操作
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
resize:
void resize(size_t n, char ch = '\0') { if (n < _size) { _size = n; _str[_size] = '\0'; } else { if (n>_capacity)//判断是否需要扩容 { reserve(n); } for (int i = _size; i < n; i++)//ch赋值给字符串 { _str[i] = ch; } _size = n; _str[_size] = '\0'; } }
判断_size是否为0
bool empty()const
{
return _size == 0;
}
push_back函数的作用就是在当前字符串的后面尾插上一个字符,先判断容量是否充足,不充足就先增容,再尾插字符串并在末位加上‘\0’
void push_back(char ch)
{
if (_size >= _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
//二倍增容
reserve(newcapacity);
}
_str[_size] = ch;// 尾插字符
_size++;// 字符量加1
_str[_size] = '\0';// 结尾添加‘\0’
}
append的作用是尾插字符串,思路和push_back一样
void append(const char* str)
{
size_t len = strlen(str);// 计算尾插字符串长度
if (_size + len >= _capacity)// 增容
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
// 因为字符串中有'\0'所以不需要额外添加
}
+=运算符实现字符串与字符之间的尾插直接调用push_back函数
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
+=运算符实现字符串与字符串之间的尾插直接调用append函数
string& operator+=(const char* str)
{
append(str);
return *this;
}
insert作用是在指定位置插入字符或字符串
insert函数用于插入字符时,首先需要判断pos的合法性,再判断当前对象能否容纳插入字符后的字符串,若不能则还需调用reserve函数进行扩容。插入字符时,先将pos位置及其后面的字符统一向后挪动一位,给待插入的字符留出位置,然后将字符插入字符串即可。
string& insert(size_t pos, char ch) { assert(pos <= _size); //检测下标的合法性 if (_size == _capacity) //判断是否需要增容 { reserve(_capacity == 0 ? 4 : _capacity * 2); } char* end = _str + _size; //将pos位置及其之后的字符向后挪动一位 while (end >= _str + pos) { *(end + 1) = *(end); end--; } _str[pos] = ch; //pos位置放上指定字符 _size++; return *this; }
insert函数用于插入字符串和插入字符思路大致相同
string& insert(size_t pos, const char* str) { assert(pos <= _size); //检测下标的合法性 size_t len = strlen(str); //计算需要插入的字符串的长度(不含'\0') if (len + _size > _capacity) //判断是否需要增容 { reserve(len + _size); } char* end = _str + _size; //将pos位置及其之后的字符向后挪动len位 while (end >= _str + pos) { *(end + len) = *(end); end--; } // 插入字符串 for (size_t i = 0; i < len; i++) { _str[i + pos] = str[i]; } _size += len; return *this; }
erase函数的作用是删除字符串任意位置开始的n个字符。
string& erase(size_t pos, size_t len = npos) { assert(pos < _size);//检测下标的合法性 if (len == npos || len + pos>_size)// 字符串不够删 { _str[pos] = '\0'; _size = pos; } else { strcpy(_str + pos, _str + pos + len); // 用有效字符串覆盖要删除的字符串 _size -= len; } return *this; }
clear作用是清空类中字符串
void clear()
{
_str[0] = '\0';
_size = 0;
}
swap函数用于交换两个对象的数据,直接调用库里的swap模板函数将对象的各个成员变量进行交换即可。但我们若是想在这里调用库里的swap模板函数,需要在swap函数之前加上“::”(作用域限定符),告诉编译器优先在全局范围寻找swap函数,否则编译器编译时会认为你调用的是正在实现的swap函数。
void swap(string& s)
{
//调用库里的swap
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
注意:若想让编译器优先在全局范围寻找某函数,则需要在该函数前面加上“::”(作用域限定符)。
将类中字符串以C语言形式输出
const char* c_str()
{
return _str;
}
[ ]运算符的重载是为了让string对象能像C字符串一样,通过[ ] +下标的方式获取字符串对应位置的字符。
// 可读可写
char& operator[](size_t i)
{
assert(i < _size); //检测下标的合法性
return _str[i]; //返回对应字符
}
// 只能读
const char& operator[](size_t i)const
{
assert(i < _size);
return _str[i];
}
size_t find(char ch, size_t pos = 0) const
{
assert(pos < _size);//检测下标的合法性
while (pos < _size)// 遍历寻找字符
{
if (_str[pos] == ch)
{
return pos;
}
pos++;
}
return npos;// 没找到返回npos
}
size_t find(const char* str, size_t pos = 0)
{
assert(pos < _size); //检测下标的合法性
const char* ret = strstr(_str + pos, str); //调用strstr进行查找
if (ret) //ret不为空指针,说明找到了
{
return ret - _str; //返回字符串第一个字符的下标
}
else
{
return npos; //没有找到返回npos
}
}
关系运算符有 >、>=、<、<=、==、!= 这六个,我们可以实现其中的>和==,剩下的六个调用这两个即可
// operator> 重载 bool operator>(const string& s1, const string& s2) { size_t i1 = 0, i2 = 0; // 两个字符串从前往后依次比较 while (i1 < s1.size() && i2 < s2.size()) { if (s1[i1]>s2[i2]) { return true; } else if (s1[i1] < s2[i2]) { return false; } else { i1++; i2++; } } // 如果il先到结尾为真,否则为假 if (i1 == s1.size()) { return false; } else { return true; } }
// operator== 重载 bool operator==(const string& s1, const string& s2) { size_t i1 = 0, i2 = 0; // 从前往后依次比较il和i2 while (i1 < s1.size() && i2 < s2.size()) { if (s1[i1] != s2[i2]) { return false; } else { i1++; i2++; } } // 如果i1和i2同时到结尾为真,否则为假 if (i1 == s1.size() && i2 == s2.size()) { return true; } else { return false; } }
剩下四个调用前两个即可
// operator!= 重载 inline bool operator!=(const string& s1, const string& s2) { return !(s1 == s2); } // operator>= 重载 inline bool operator>=(const string& s1, const string& s2) { return s1>s2 || s1 == s2; } // operator< 重载 inline bool operator<(const string& s1, const string& s2) { return !(s1 >= s2); } // operator<= 重载 inline bool operator<=(const string& s1, const string& s2) { return !(s1 > s2); }
重载>>运算符是为了让string对象能够像内置类型一样使用>>运算符直接输入。输入前我们需要先将对象的C字符串置空,然后从标准输入流读取字符,直到读取到’ ‘或是’\n’便停止读取。
istream& operator>>(istream& in, string& s)
{
s.clear(); // 清空字符串
char ch;
ch = in.get(); // 读取一个字符
// 当读取到的字符不是空格或'\n'的时候继续读取
while (ch != ' '&&ch != '\n')
{
s += ch;
ch = in.get();
}
return in;
}
重载<<运算符是为了让string对象能够像内置类型一样使用<<运算符直接输出打印。实现时我们可以直接使用范围for对对象进行遍历即可。
ostream& operator<<(ostream& out, const string& s)
{
// 遍历输出
for (size_t i = 0; i < s.size(); i++)
{
out << s[i];
}
return out;
}
getline函数用于读取一行含有空格的字符串。实现时于>>运算符的重载基本相同,只是当读取到’\n’的时候才停止读取字符。
istream& getline(istream& in, string& s)
{
s.clear();
char ch;
ch = in.get();
while (ch != '\n')// 当读取到的字符不是'\n'的时候继续读取
{
s += ch;
ch = in.get();
}
return in;
}
namespace nzb { class string { public: typedef char* iterator; typedef const char* const_iterator; // 默认成员函数 string(char* str = "") :_size(strlen(str))//记录字符串长度 , _capacity(_size)//设定容量 { _str = new char[_capacity + 1];//为存储字符串开辟空间(多开一个用于存放'\0') strcpy(_str, str);//将字符串拷贝到已开辟好的空间 } string(const string& s) :_str(nullptr) { string tmp(s._str); swap(tmp); } string& operator=(string s) { swap(s); return *this; } ~string() { delete[] _str; _str = nullptr; } // 迭代器相关函数 iterator begin() { return _str; } iterator end() { return _str + _size; } const_iterator begin() const { return _str; } const_iterator end() const { return _str + _size; } // 容量相关函数 size_t size() const { return _size; } size_t capacity() const { return _capacity; } // 扩容 void reserve(size_t n) { if (n > _capacity) { char* tmp = new char[n + 1]; strcpy(tmp, _str); delete[] _str; _str = tmp; _capacity = n; } } void resize(size_t n, char ch = '\0') { if (n < _size) { _size = n; _str[_size] = '\0'; } else { if (n>_capacity) { reserve(n); } for (int i = _size; i < n; i++) { _str[i] = ch; } _size = n; _str[_size] = '\0'; } } // 判空 bool empty()const { return _size == 0; } // 修改字符,字符串相关函数 // 插入字符 void push_back(char ch) { if (_size >= _capacity) { size_t newcapacity = _capacity == 0 ? 4 : _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+=(char ch) { push_back(ch); return *this; } // +=字符串 string& operator+=(const char* str) { append(str); return *this; } // 在指定位置插入字符 string& insert(size_t pos, char ch) { assert(pos <= _size); if (_size == _capacity) { size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2; reserve(newcapacity); } int end = _size + 1; while (pos < end) { _str[end] = _str[end - 1]; end--; } _str[pos] = ch; _size++; return *this; } // 在指定位置插入字符串 string& insert(size_t pos, const char* str) { assert(pos <= _size); size_t len = strlen(str); if (len == 0) { return *this; } if (len + _size>_capacity) { reserve(len + _size); } size_t end = len + _size; while (end >= pos + len) { _str[end] = _str[end - len]; --end; } for (size_t i = 0; i < len; i++) { _str[i + pos] = str[i]; } _size += len; return *this; } // 删除数据 string& erase(size_t pos, size_t len = npos) { assert(pos < _size); if (len == npos || len + pos>_size) { _str[pos] = '\0'; _size = pos; } else { strcpy(_str + pos, _str + pos + len); _size -= len; } return *this; } // 输出字符串 const char* c_str() { return _str; } // 清除数据 void clear() { _str[0] = '\0'; _size = 0; } // 交换 void swap(string& s) { ::swap(_str, s._str); ::swap(_size, s._size); ::swap(_capacity, s._capacity); } // 访问字符串相关函数 // 支持读写 char& operator[](size_t pos) { return _str[pos]; } // 只支持读 const char& operator[](size_t pos) const { return _str[pos]; } // 查找字符 size_t find(char ch, size_t pos = 0) const { assert(pos < _size); while (pos < _size) { if (_str[pos] == ch) { return pos; } pos++; } return npos; } // 查找字符串 size_t find(const char* str, size_t pos = 0) const { assert(pos < _size); const char* ret = strstr(_str + pos, str); if (ret) { return ret - _str; } else { return npos; } } private: char* _str; size_t _size; size_t _capacity; static const size_t npos; }; const size_t string::npos = -1; //关系运算符重载函数 bool operator>(const string& s1, const string& s2) { size_t i1 = 0, i2 = 0; while (i1 < s1.size() && i2 < s2.size()) { if (s1[i1]>s2[i2]) { return true; } else if (s1[i1] < s2[i2]) { return false; } else { i1++; i2++; } } if (i1 == s1.size()) { return false; } else { return true; } } bool operator==(const string& s1, const string& s2) { size_t i1 = 0, i2 = 0; while (i1 < s1.size() && i2 < s2.size()) { if (s1[i1] != s2[i2]) { return false; } else { i1++; i2++; } } if (i1 == s1.size() && i2 == s2.size()) { return true; } else { return false; } } inline bool operator!=(const string& s1, const string& s2) { return !(s1 == s2); } inline bool operator>=(const string& s1, const string& s2) { return s1>s2 || s1 == s2; } inline bool operator<(const string& s1, const string& s2) { return !(s1 >= s2); } inline bool operator<=(const string& s1, const string& s2) { return !(s1 > s2); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。