十四、比较大小
1.operator<
如下代码所示,我们先进行依次比较,若前面可以分出胜负自然好说,但若分不出胜负,那么就只有一种情况是满足小于的情况,即第一个没有值了,第二个还有值,其余皆为不满足小于的情况
bool operator<(const string& s) const { size_t i1 = 0; size_t i2 = 0; while (i1 < _size && i2 < s._size) { if (_str[i1] < s._str[i2]) { return true; } else if (_str[i1] > s._str[i2]) { return false; } else { i1++; i2++; } } return (i1 == _size) && (i2 != s._size); //return _size<s._size; }
上面是我们自己手撕的代码,同样的我们也可以利用c语言中的一些库来更方便的完成这个代码
下面这串代码虽然短小,但是可能不是很好理解,我们第一行的目的就是先求出两个对象最小的size,然后让两个对象的前size个进行比较,如果相等的话,那么ret就是0,我们就只需要确认一下第一个的size是否小于第二个的size即可。如果不相等,那么我们看ret是否小于0,小于既是正确的
bool operator<(const string& s) const { int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size); return ret == 0 ? _size < s._size : ret < 0; }
2.operator==(const string& s)
这个就比较简单了,我们先比较size,如果相等的条件下,两个串也相等,那么就是相等了
bool operator==(const string& s) const { return _size == s._size && (memcmp(_str, s._str, _size) == 0); }
3.其他比较
解决了前两个,其实其他的就可以直接进行复用了,如下代码所示
bool operator<=(const string& s) const { return (*this < s) || (*this == s); } bool operator>=(const string& s) const { return !(*this < s); } bool operator>(const string& s) const { return !(*this <= s); } bool operator!=(const string& s) const { return !(*this == s); }
十五、赋值运算符重载
1.传统写法
如下所示,是我们比较传统的写法,它跟拷贝构造函数是差不多的,我们不管原来的是什么样子,反正总归最后基本就是把右边的复制了一份。那么我们就直接开空间,释放原来的空间,随之拷贝数据即可。它与拷贝构造函数的区别就在于,它是已经存在的两个变量之间的赋值拷贝,这个过程是需要释放掉被赋值的对象原来的数据的。而拷贝构造函数是用一个已经存在的去构造一个不存在的,以前这个也不存在,所以就不需要释放空间。
总归就是一个深拷贝的写法
//赋值运算符重载 string& operator=(const string& s) { if (this != &s) { char* tmp = new char[s._capacity + 1]; delete[] _str; _str = tmp; memcpy(_str, s._str, s._size + 1); _size = s._size; _capacity = _capacity; } return *this; }
2.现代写法
如下代码所示,这段代码就充分利用了拷贝构造函数,然后移花接木即可,顺便原来的空间也可以因为tmp的析构而带走,充分的榨干了tmp的作用。
string& operator=(const string& s) { if (this != &s) { string tmp(s); std::swap(_str, tmp._str); std::swap(_size, tmp._size); std::swap(_capacity, tmp._capacity); } return *this; }
但是要注意,我们不可以这样写:
程序会崩溃的
这是因为swap函数中是通过三次赋值操作来实现的,而赋值操作又通过swap来实现,实现了死递归。导致栈溢出了
事实上,库里面也自己提供了两个string对象的交换
而我们如果需要自己手动实现swap的话,也正好就是前面的代码
void swap(string& s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } string& operator=(const string& s) { if (this != &s) { string tmp(s); swap(tmp); } return *this; }
上面这段代码还可以继续进行简化
string& operator=(string s) { swap(s); return *this; }
这段代码我们就厉害了,直接在传参的过程中调用拷贝构造,可谓精简到极致了
十六、拷贝构造的现代写法
如下代码所示,是我们较为传统的写法,也是上面的写法。
//拷贝构造函数 string(const string& s) { _str = new char[s._capacity + 1]; memcpy(_str, s._str, s._size + 1); _size = s._size; _capacity = s._capacity; }
上面这段代码,其实也可以进一步的进行简化。即现代写法
string(const string& s) :_str(nullptr) ,_size(0) ,_capacity(0) { string tmp(s._str); swap(tmp); }
注意在这段代码中,初始化列表必须得写法,否则在调用赋值运算符重载的过程中,由于this所指向的对象未初始化,导致其的_str随意指向一块空间,交换以后,析构的过程中直接崩溃。故必须得加初始化
其次在这段代码中,还有一个问题是如果s中的_str中间有一个’\0’字符,那么也会出现问题。导致后面的部分无法拷贝上去。
综上所述,对于拷贝构造函数,还是传统写法更优一些
十七、string模拟实现完整代码
下面是类实现的具体代码
#pragma once #include<iostream> #include<string.h> #include<assert.h> using namespace std; namespace String { class string { public : /*********************************************/ //迭代器 typedef char* iterator; iterator begin() { return _str; } iterator end() { //return _str + strlen(_str); return _str + _size; } //const 迭代器 typedef const char* const_iterator; const_iterator begin() const { return _str; } const_iterator end() const { //return _str + strlen(_str); return _str + _size; } /**************************************************************************************/ //分开的构造函数写法 给一个字符串去构造一个string //string(const char* str) // :_str(new char[strlen(str) + 1]) // , _size(strlen(str)) // , _capacity(strlen(str)) //{ // strcpy(_str, str); //} 无参的默认构造函数 //string() // :_str(new char[16]) // ,_size(0) // ,_capacity(15) //{ // memcpy(_str, "\0\0\0\0", 4); //} /**************************************************************************************/ //以上合二为一的构造函数写法 string(const char* str = "") :_str(new char[strlen(str) + 1]) , _size(strlen(str)) , _capacity(strlen(str)) { //strcpy(_str, str); memcpy(_str, str, _size + 1); } //拷贝构造函数 string(const string& s) { _str = new char[s._capacity + 1]; memcpy(_str, s._str, s._size + 1); _size = s._size; _capacity = s._capacity; } //string(const string& s) // :_str(nullptr) // ,_size(0) // ,_capacity(0) //{ // string tmp(s._str); // swap(tmp); //} /**************************************************************************************/ //析构函数 ~string() { delete[] _str; _str = nullptr; _size = 0; _capacity = 0; } /**************************************************************************************/ //获取string中的字符串 const char* c_str() const { return _str; } /**************************************************************************************/ //获取当前有效元素个数 size_t size() const { return _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]; } /**************************************************************************************/ //增删查改之增 void reserve(size_t n) { if (n > _capacity) { char* tmp = new char[n + 1]; //strcpy(tmp, _str); memcpy(tmp, _str, _size + 1); _capacity = n; delete[] _str; _str = tmp; } } //pushback void pushback(char ch) { if (_size == _capacity) { int newcapacity = _capacity == 0 ? 4 : _capacity * 2; reserve(newcapacity); } _str[_size++] = ch; _str[_size] = '\0'; } void append(const char* str) { int len = strlen(str); if (len + _size > _capacity) { int newcapacity = (len+_size) ; reserve(newcapacity); } //while (len--) //{ // _str[_size++] = *str++; //} //_str[_size] = '\0'; //strcpy(_str + _size, str); memcpy(_str + _size, str, len + 1); _size += len; } string& operator+=(char ch) { pushback(ch); return *this; } string& operator+=(const char* str) { append(str); return *this; } void insert(size_t pos, size_t n, char ch) { assert(pos <= _size); if (_size + n > _capacity) { int newcapacity = _size + n; reserve(newcapacity); } int count = _size - pos + 1; int size = _size; while (count--) { _str[size + n] = _str[size]; size--; } int i = pos; for (i = pos; i < pos + n; i++) { _str[i] = ch; } _size += n; } void insert(size_t pos, const char* str) { assert(pos <= _size); int len = strlen(str); if (_size + len > _capacity) { int newcapacity = _size + len; reserve(newcapacity); } int count = _size - pos + 1; int size = _size; while (count--) { _str[size + len] = _str[size]; size--; } int i = pos; for (i = pos; i < pos + len; i++) { _str[i] = *str; str++; } _size += len; } void insert1(size_t pos, size_t n, char ch) { assert(pos <= _size); if (_size + n > _capacity) { int newcapacity = _size + n; reserve(newcapacity); } int end = _size; while (end >= (int)pos) { _str[end + n] = _str[end]; end--; } int i = pos; for (i = pos; i < pos + n; i++) { _str[i] = ch; } _size += n; } void insert2(size_t pos, size_t n, char ch) { assert(pos <= _size); if (_size + n > _capacity) { int newcapacity = _size + n; reserve(newcapacity); } int end = _size; while (end >= pos && end != npos) { _str[end + n] = _str[end]; end--; } int i = pos; for (i = pos; i < pos + n; i++) { _str[i] = ch; } _size += n; } /*****************************************************************************/ //增删查改之删 void erase(int pos, size_t len = npos) { assert(pos <= _size); if (len == npos || pos + len > _size) { _str[pos] = '\0'; _size = pos; } else { int end = pos + len; while (end <= _size) { _str[pos++] = _str[end++]; } _size -= len; } } void clear() { _str[0] = '\0'; _size = 0; } /*****************************************************************************/ //查 size_t find(char ch, size_t pos = 0) { assert(pos < _size); for (int i = pos; i < _size; i++) { if (_str[i] == ch) { return i; } } return npos; } size_t find(const char* str, size_t pos = 0) { assert(pos < _size); char* ptr = strstr(_str + pos, str); if (ptr) { return (ptr - _str); } else { return npos; } } //提取出指定位置的串 string substr(size_t pos = 0, size_t len = npos) { assert(pos < _size); size_t n = len; if (len == npos || pos + len > _size) { n = _size - pos; } string tmp; tmp.reserve(n); for (size_t i = pos; i < pos + n; i++) { tmp += _str[i]; } return tmp; } //调整size void resize(size_t n, char ch = '\0') { if (n <= _size) { _size = n; _str[_size] = '\0'; } else { reserve(n); for (int i = _size; i < n; i++) { _str[i] = ch; } _size = n; _str[_size] = '\0'; } } //比较大小 //bool operator<(const string& s) //{ // size_t i1 = 0; // size_t i2 = 0; // while (i1 < _size && i2 < s._size) // { // if (_str[i1] < s._str[i2]) // { // return true; // } // else if (_str[i1] > s._str[i2]) // { // return false; // } // else // { // i1++; // i2++; // } // } // return (i1 == _size) && (i2 != s._size); // //return _size<s._size; //} bool operator<(const string& s) const { int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size); return ret == 0 ? _size < s._size : ret < 0; } bool operator==(const string& s) const { return _size == s._size && (memcmp(_str, s._str, _size) == 0); } bool operator<=(const string& s) const { return (*this < s) || (*this == s); } bool operator>=(const string& s) const { return !(*this < s); } bool operator>(const string& s) const { return !(*this <= s); } bool operator!=(const string& s) const { return !(*this == s); } //赋值运算符重载 //string& operator=(const string& s) //{ // if (this != &s) // { // char* tmp = new char[s._capacity + 1]; // delete[] _str; // _str = tmp; // memcpy(_str, s._str, s._size + 1); // _size = s._size; // _capacity = _capacity; // } // return *this; //} void swap(string& s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } //string& operator=(const string& s) //{ // if (this != &s) // { // string tmp(s); // swap(tmp); // } // return *this; //} string& operator=(string s) { swap(s); return *this; } private: char* _str; size_t _size; size_t _capacity; public: const static size_t npos; }; const size_t string::npos = -1; ostream& operator<<(ostream& out, const string& s) { //for (size_t i = 0; i < s.size(); i++) //{ // out << s[i]; //} //out << endl; //return out; for (auto ch : s) { out << ch; } out << endl; return out; } istream& operator>>(istream& in, string& s) { s.clear(); char ch = in.get(); while (ch == ' ' || ch == '\n') { ch = in.get(); } char buff[128] = { 0 }; int i = 0; while (ch != ' ' && ch != '\n') { //s += ch; buff[i++] = ch; if (i == 127) { buff[i] = '\0'; s += buff; i = 0; } ch = in.get(); } if (i != 0) { buff[i] = '\0'; s += buff; } return in; } }
下面是测试的代码
#define _CRT_SECURE_NO_WARNINGS 1 #include "String.h" #include <string> void teststring1() { String::string s1("hello world"); cout << s1.c_str() << endl; String::string s2; cout << s2.c_str() << endl; //string s; //cout << s << endl; int i = 0; //for (i = 0; i < s1.size(); i++) //{ // s1[i]++; //} //cout << endl; for (i = 0; i < s1.size(); i++) { cout << s1[i] << ' '; } cout << endl; //const String::string s3("hello hello"); //for (i = 0; i < s3.size(); i++) //{ // s3[i]++; //} //cout << endl; String::string::iterator it = s1.begin(); while (it != s1.end()) { cout << *it << ' '; it++; } cout << endl; for (auto ch : s1) { cout << ch << ' '; } } void teststring2() { const String::string s1("hello world"); String::string::const_iterator it = s1.begin(); while (it != s1.end()) { cout << *it << ' '; it++; } cout << endl; for (auto ch : s1) { cout << ch << ' '; } } void teststring3() { String::string s1("hello world"); cout << s1.c_str() << endl; s1.pushback('!'); cout << s1.c_str() << endl; s1.append("hello world"); cout << s1.c_str() << endl; String::string s2("hello world"); cout << s2.c_str() << endl; s2 += '!'; cout << s2.c_str() << endl; s2 += "hello world"; cout << s2.c_str() << endl; String::string s3("hello world"); cout << s3.c_str() << endl; s3.insert(0, 5, 'x'); cout << s3.c_str() << endl; s3.insert1(0, 5, 'e'); cout << s3.c_str() << endl; s3.erase(5, 4); cout << s3.c_str() << endl; } void teststring4() { String::string url = "ftp://www.baidu.com/?tn=65081411_1_oem_dg"; size_t pos1 = url.find("://"); if (pos1 != String::string::npos) { String::string protocol = url.substr(0, pos1); cout << protocol.c_str() << endl; } size_t pos2 = url.find('/', pos1 + 3); if (pos2 != String::string::npos) { String::string domain = url.substr(pos1 + 3, pos2 - (pos1 + 3)); String::string uri = url.substr(pos2 + 1); cout << domain.c_str() << endl; cout << uri.c_str() << endl; } } void teststring5() { String::string s("hello world"); s.resize(8); cout << s.c_str() << endl; s.resize(13, 'x'); cout << s.c_str() << endl; s.resize(20, 'y'); cout << s.c_str() << endl; cout << s; } void teststring6() { String::string s("hello world"); s += '\0'; s += "xxxxxx"; cout << s.c_str() << endl; cout << s << endl; } void teststring7() { String::string s; cin >> s; cout << s << endl; cin >> s; cout << s << endl; } void teststring8() { //String::string s1("hello world"); //s1 += '\0'; //s1 += "xxxxx"; //String::string s2("hello world"); //s2 += '\0'; //s2 += "yyyyy"; //cout << (s2 < s1) << endl; //cout << (s2 == s1) << endl; String::string s1("hello"); String::string s2("hello"); cout << (s1 < s2) << endl; cout << (s1 > s2) << endl; cout << (s1 == s2) << endl << endl; String::string s3("hello"); String::string s4("helloxxx"); cout << (s3 < s4) << endl; cout << (s3 > s4) << endl; cout << (s3 == s4) << endl << endl; String::string s5("helloxxx"); String::string s6("hello"); cout << (s5 < s6) << endl; cout << (s5 > s6) << endl; cout << (s5 == s6) << endl << endl; s6 = s5; cout << s6 << endl; } int main() { //teststring1(); //teststring2(); //teststring3(); //teststring4(); teststring8(); return 0; }
好了本期内容就到这里了
本期内容确实比较硬核,希望读者能够认真消化
好了我们下期内容再见!!!