文章目录
拷贝构造和赋值重载的传统写法vs现代写法
传统写法
//传统写法
//拷贝构造
string(const string& s1)
{
assert(s1._str);
_size = _capacity = strlen(s1._str);
_str = new char[_capacity + 1];
strcpy(_str, s1._str);
}
//赋值重载
string& operator= (const string& s1)
{
//深拷贝
delete[]_str;
_size = _capacity = strlen(s1._str);
_str = new char[_capacity + 1];
strcpy(_str, s1._str);
return *this;
}
现代写法:
//现代写法
//拷贝构造
string(const string& s1)
:_str(nullptr)
{
assert(s1._str);//不能直接写成s1,会陷入无限递归的拷贝
string tmp(s1._str);
std::swap(_str, tmp._str);
_size = _capacity = strlen(_str);
}
//赋值重载
string& operator= (const string& s1)
{
string tmp(s1);
std::swap(_str, tmp._str);
_capacity = _size = strlen(_str);
return *this;
}
拷贝构造和赋值重载的交换
//实现直接交换类的swap,库里的swap也能完成,但要完成三次深拷贝,效率低下
//系统提供的:
/* template <class T> void swap(T&a, T&b)
{
//string的拷贝,三次深拷贝
T c(a);
a = b;
b = c;
}
*/
//自己实现的
void swap(string& s)
{
//swap使用后,在局部作用域会默认只使用一种参数类型的交换,所以要用::来表明它是全局的,不受局部限制
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
拷贝构造和赋值重载的现代写法可以复用swap
//现代写法
//拷贝构造
string(const string& s1)
:_str(nullptr)
{
assert(s1._str);//不能直接写成s1,会陷入无限递归的拷贝
string tmp(s1._str);
swap(tmp);
}
//赋值重载
string& operator= (const string& s1)
{
string tmp(s1);
swap(tmp);
return *this;
}
范围for
如果我们把迭代器的名称一改,由begin->Begin,end->End,范围for就实现不了了
所以说范围for的底层是由迭代器实现的
“增”的操作(从尾部添加)
字符:push_back
字符串:append
还有一个很好用的operator+=,可以复用上面两个函数
既然要添加字符,就不得不涉及到增容:reserve
reserve实现
//reserve
void reserve(size_t capacity)
{
if (capacity > _capacity)
{
char* tmp = new char[capacity + 1];
strncpy(tmp, _str, _size + 1);//要用strncpy或memcpy拷贝,如果用strcpy,可能源字符串是"hello\0\0\0\0",这样就会导致拷贝的内容不一致
delete[] _str;
_str = tmp;
_capacity = capacity;
}
}
既然有reserve,那么也得提到resize
resize的实现:
//resize
void resize(int n, char x = '\0')
{
//有三种情况:
//1.n <= _size,将n赋值给_size,_str[_size] = '\0'
//2._size < n <= _capacity,循环将多余的位置设置为'\0'
//3.n > _capacity,先reserve(n),再将多余的位置设置为'\0'
if (n <= _size)
{
_size = n;
_str[_size] = '\0';
}
else if (n <= _capacity)
{
int end = n;
while (end > _size)
{
_str[end] = '\0';
end--;
}
_size = n;
}
else
{
reserve(n);
int end = n;
while (end > _size)
{
_str[end] = '\0';
--end;
}
_size = n;
}
}
“改”的实现:insert
特殊情况:pos和是size_t的类型且pos为0时
<<、>>及inline的重载
<<容易实现,>>的输入遇到空格和回车时会停止读取,所以采用循环读取字符的方式,当字符为空格或回车时,就停止输入,可以用cin.get()接口来实现字符的读取,类似getchar
getline的原理与>>类似,遇到回车才停止输入。
注意:>>和getline的输入是覆盖输入的,所以输入前需要用clear接口清空
总体实现:
#pragma once
#include<iostream>
#include<string.h>
#include<assert.h>
using namespace std;
namespace ysj{
class string {
public:
typedef char* iterator;
//*****************************************************************************************************************************
//构造函数
string(const char* str = "")//默认缺省值不给nullptr,因为下面要将str作为strlen的参数,会引发错误
{
_size = _capacity = strlen(str);//_capacity是指有效容量,不包括\0,但实际开的空间要多一个字节容纳\0
_str = new char[_capacity + 1];
strcpy(_str, str);
}
//析构函数
~string()
{
delete[] _str;
_str = nullptr;
_capacity = _size = 0;
}
//传统写法
//拷贝构造
/*string(const string& s1)
{
assert(s1._str);
_size = _capacity = strlen(s1._str);
_str = new char[_capacity + 1];
strcpy(_str, s1._str);
}*/
//赋值重载
//string& operator= (const string& s1)
//{
// //深拷贝
// delete[]_str;
//
// _size = _capacity = strlen(s1._str);
// _str = new char[_capacity + 1];
// strcpy(_str, s1._str);
// return *this;
//}
//现代写法
//拷贝构造
string(const string& s1)
:_str(nullptr)
{
assert(s1._str);//不能直接写成s1,会陷入无限递归的拷贝
//string tmp(s1._str);
//std::swap(_str, tmp._str);
//_size = _capacity = strlen(_str);
string tmp(s1._str);
swap(tmp);
}
//赋值重载
string& operator= (const string& s1)
{
/*string tmp(s1);
std::swap(_str, tmp._str);
_capacity = _size = strlen(_str);*/
string tmp(s1);
swap(tmp);
return *this;
}
//实现直接交换类的swap,库里的swap也能完成,但要完成三次深拷贝,效率低下
/* template <class T> void swap(T&a, T&b)
{
//string的拷贝,三次深拷贝
T c(a);
a = b;
b = c;
}
*/
void swap(string& s)
{
//swap使用后,在局部作用域会默认只使用一种参数类型的交换,所以要用::来表明它是全局的,不受局部限制
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
//*********************************************************************************************************************
//*********************************************************************************************************************
//查
//1.[]重载
//可读可写返回
char& operator[](size_t pos)
{
assert(pos <= size());
return _str[pos];
}
//只读返回
char& operator[](size_t pos)const
{
assert(pos <= size());
return _str[pos];
}
//2.迭代器
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
//const迭代器
const iterator begin()const
{
return _str;
}
const iterator end()const
{
return _str + _size;
}
//返回string的有效字符数字
size_t size()
{
return _size;
}
//3.范围for,其本质就是用迭代器实现的
//万一operatoe[]想以只读的方式返回,size()也要是const的
size_t size()const
{
return _size;
}
//*********************************************************************************************************************
//*********************************************************************************************************************
//增
//1.push_back
void push_back(char x)
{
//判断增容,若需要增容,使用reserve增容
if (_size == _capacity)
{
int capacity = (_capacity == 0 ? 4 : 2 * _capacity);
reserve(capacity);
}
_str[_size] = x;
_str[_size + 1] = '\0';
++_size;
}
//2.append
void append(const char* str)
{
int len = strlen(str) + _size;
if (len > _capacity)
{
reserve(len);
}
strncpy(_str + _size, str, strlen(str) + 1);
_size = len;
_str[_size] = '\0';
}
//3.operator+=
//可复用append和push_back的代码
//字符
string& operator+=(char x)
{
push_back(x);
return *this;
}
//字符串
string& operator+=(const char* str)
{
append(str);
return *this;
}
//reserve
void reserve(size_t capacity)
{
if (capacity > _capacity)
{
char* tmp = new char[capacity + 1];
strncpy(tmp, _str, _size + 1);//要用strncpy或memcpy拷贝,如果用strcpy,可能源字符串是"hello\0\0\0\0",这样就会导致拷贝的内容不一致
delete[] _str;
_str = tmp;
_capacity = capacity;
}
}
//resize
void resize(int n, char x = '\0')
{
//有三种情况:
//1.n <= _size,将n赋值给_size,_str[_size] = '\0'
//2._size < n <= _capacity,循环将多余的位置设置为'\0'
//3.n > _capacity,先reserve(n),再将多余的位置设置为'\0'
if (n <= _size)
{
_size = n;
_str[_size] = '\0';
}
else if (n <= _capacity)
{
int end = n;
while (end > _size)
{
_str[end] = '\0';
end--;
}
_size = n;
}
else
{
reserve(n);
int end = n;
while (end > _size)
{
_str[end] = '\0';
--end;
}
_size = n;
}
}
//*********************************************************************************************************************
//*********************************************************************************************************************
//改,insert,在pos之前插入
//字符
void insert(char x, size_t pos)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(2 * _capacity);
}
size_t end = _size + 1;//如end等于_size+1,当pos等于0时,下面的while循环中,end永远不会小于0,就陷入了死循环
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = x;
++_size;
}
//字符串
void insert(const char* s, size_t pos)
{
assert(pos <= _size);
int len = strlen(s);
if (len + _size > _capacity)
{
reserve(len + _size);
}
size_t end = _size + 1;//如end等于_size+1,当pos等于0时,下面的while循环中,end永远不会小于0,就陷入了死循环
while (end > pos)
{
_str[end + len - 1] = _str[end - 1];
--end;
}
strncpy(_str + pos, s, strlen(s));
_size += len;
}
//*********************************************************************************************************************
//*********************************************************************************************************************
//删,erase,从pos位置开始删除
///默认从开头删
string& erase(size_t pos = 0, size_t len = npos)//静态缺省变量npos,表示当没有给出删除的位置时,默认从npos开始,也就是字符串的末尾
{
assert(pos < _size);
if (len > _size - pos)//剩余字符长度小于删除字符长度
{
//将从pos位置开始的之后所有字符都删除
_str[pos] = '\0';
_size = pos;
}
else//向前挪
{
strncpy(_str + pos, _str + pos + len, strlen(_str + pos + len) + 1);
_size -= len;
}
return *this;
}
//*********************************************************************************************************************
//*********************************************************************************************************************
//find、rfind的实现
//find
//字符
size_t find(char x, size_t pos = 0)//找到了返回下标,没找到返回npos
{
for (int i = pos; i < _size; ++i)
{
if (x == _str[i])
return i;
}
return npos;
}
//字符串
size_t find(const char*s, size_t pos = 0)
{
char* ret = nullptr;
//用strstr查找
for (int i = pos; i < _size; ++i)
{
ret = strstr(_str + i, s);
if (ret)
{
return ret - _str;
}
}
return npos;
}
//refind
//字符
size_t rfind(char x, size_t pos = npos)//默认从最后开始查找
{
if (pos >= _size)
{
pos = _size - 1;
}
for (int i = pos; i >= 0; --i)
{
if (_str[i] == x)
{
return i;
}
}
return npos;
}
//字符串
size_t rfind(const char* s, size_t pos = npos)
{
if (pos >= _size)
{
pos = _size - 1;
}
char* ret = nullptr;
for (int i = pos; i >= 0; --i)
{
ret = strstr(_str + i, s);
if (ret)
{
return ret - _str;
}
}
return npos;
}
//*********************************************************************************************************************
//clear重载,清空字符串
void clear()
{
_size = 0;
_str[0] = '\0';
}
//*********************************************************************************************************************
//>的重载
bool operator>(const string& s)const
{
assert(s._str);
return strcmp(_str, s._str) > 0;
}
//==的重载
bool operator==(const string& s)const
{
assert(s._str);
return strcmp(_str, s._str) == 0;
}
//<的重载
bool operator<(const string& s)const
{
assert(s._str);
return (!(*this > s)) && (!(*this == s));
}
//>=的重载
bool operator<=(const string& s)const
{
assert(s._str);
return (*this < s) || (*this == s);
}
//<=的重载
bool operator>=(const string& s)const
{
assert(s._str);
return (*this > s) || (*this == s);
}
//!=的重载
bool operator!=(const string& s)const
{
assert(s._str);
return !(*this == s);
}
//*********************************************************************************************************************
//*********************************************************************************************************************
void Print()
{
for (int i = 0; i < size(); ++i)
cout << _str[i];
cout << endl;
}
private:
char* _str;
int _size;
int _capacity;
static const size_t npos;
};
const size_t string::npos = -1;
//<< >>的重载
//<<的重载
ostream& operator<<(ostream& out, const string& s)
{
for (int i = 0; i < s.size(); ++i)
{
out << s[i];
}
return out;
}
//>>的重载
istream& operator>>(istream& in, string& s)
{
s.clear();
char ch = in.get();//类似getchar,将字符串输入缓冲区,再一个字符一个字符读取
while (ch != '\n' && ch != ' ')//只要不是回车或空格,就继续输入
{
s += ch;
ch = in.get();
}
return in;
}
istream& getline(istream& in, string& s)
{
s.clear();
char ch = in.get();//类似getchar,将字符串输入缓冲区,再一个字符一个字符读取
while (ch != '\n')//只要不是回车或空格,就继续输入
{
s += ch;
ch = in.get();
}
return in;
}
//测试
//默认成员函数的测试
void TestString1()
{
string s1;
string s2("hello world");
string s3(s2);
string s4;
s1.Print();
s2.Print();
s3.Print();
s1 = s3;
s1.Print();
s4.swap(s3);
s4.Print();
}
//查找的测试,operator[],迭代器,范围for
void TestString2()
{
string s1("hello world");
for (size_t i = 0; i <= s1.size(); ++i)
{
cout << s1[i];
}
cout << endl;
string::iterator begin = s1.begin();
for (; begin != s1.end(); ++begin)
{
cout << *begin;
}
cout << endl;
for (auto e : s1)
{
cout << e;
}
cout << endl;
}
//增的测试,push_back,append,operator+=,reserve,resize
void TestString3()
{
string s1("hello world");
string s2 = ("hello string");
string s3 = ("hello ");
s1.push_back('a');
s2.append("tttt");
s3 += "world";
s1.Print();
s2.Print();
s3.Print();
s1.resize(5);
s1 += "#####";
s2.resize(20);
s2 += "####";
s3.resize(20);
s3 += "####";
s1.Print();
s2.Print();
s3.Print();
}
//改的测试,insert
void TestString4()
{
string s1("hello");
string s2(s1);
s2.insert('a', 0);
s2.Print();
s2.insert('a', 3);
s2.Print();
s2.insert('a', s2.size());
s2.Print();
s1.insert("abc", 0);
s1.Print();
s1.insert("abc", 3);
s1.Print();
s1.insert("abc", s1.size());
s1.Print();
}
//删的测试,erase
void TestString5()
{
string s1("hello world");
string s2(s1);
string s3(s1);
s1.erase();
s2.erase(0, 2);
s3.erase(3);
s1.Print();
s2.Print();
s3.Print();
}
//find,refind的测试
void TestString6()
{
string s1("http://www.cplusplus.com/");
cout << s1.find('a') <<endl;
cout << s1.find("plus") << endl;
string s2("http://www.cplusplus.com/");
cout << s2.rfind('a') << endl;
cout << s2.rfind("plus") << endl;
}
//< > == >= <= !=重载测试
void TestString7()
{
string s1("hello");
string s2("hello");
string s3("hellp");
cout << (s1 == s2) << endl;
cout << (s1 > s2) << endl;
cout << (s1 < s2) << endl;
cout << (s1 == s3) << endl;
cout << (s1 > s3) << endl;
cout << (s1 < s3) << endl;
}
//<< >> 的重载测试
void TestString8()
{
string s1("hello");
cout << s1 << endl;;
string s2("hello world haha");
cout << s2 << endl;
cin >> s2;
cout << s2 << endl;
getline(cin, s2);
cout << s2 << endl;
}
}