一般来说,为了防止与标准库中的string冲突,我们都会开一个命名空间。
构造函数,如果用最基本的构造函数,会怎么样
class string
{
public:
string()
:_str(nullptr)
{}
string(char* s)
:_str(s)
{
}
这样子初始化的常量字符串,不能被修改,被存放再代码段,且扩容也不好处理。因此构造时候,不能直接传,要用new来动态开辟空间储存,因此其实string的底层是数组。
string(const char* s = "")
:_str(new char[strlen(s) + 1])
{
strcpy(_str, s);
}
而析构则使用相对应的函数
~string()
{
delete[] _str;
_str = nullptr;
}
当无参数时候,是空字符串,这样strlen就会为0,不然如果是空指针,则会出现空指针的解引用。
拷贝构造:
因为底层是一个字符指针,所以指针的拷贝,不能用浅拷贝,需用深拷贝。
string(const string& s)
:_str(new char [strlen(s._str)+1])
{
strcpy(_str,s._str);
}
这里便是深拷贝的写法,这里,主要利用的是,在类域中,可以直接访问对象的私有变量,即拷贝构造函数中,可以直接访问另一个引用同类对象的私有成员。
同样的operator=也需要深拷贝。
string& operator=(const string& s)
{
if (&s != this)
{
char* tmp = new char[strlen(s._str) + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
}
return *this;
}
而深拷贝也具有现代写法,是先构造一个相同对象,然后交换,宛如狸猫换太子。
string(const string& s)
:_str(nullptr)
{
string tmp(s._str);
swap(_str,_tmp._str);
}
string& operator=(string s)
{
swap(_str,s._str);
return *this;
}
赋值的现代写法,巧妙利用拷贝构造,直接换指针,这样不会影响原来的对象,因为那是形参。
当然这只是简单的string,只是实现了,基础的默认成员函数。
namespace whc
{
class string
{
public:
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
string(const char* s = "")
:_str(new char[strlen(s) + 1])//因为有\0占一个位置
,_size(strlen(s))
,_capacity(strlen(s))
{
strcpy(_str, s);
}
~string()
{
_size = _capacity = 0;
delete[] _str;
_str = nullptr;
}
string(const string& s)
:_str(new char[strlen(s._str)+1])
,_size(s._size)
,_capacity(s._capacity)
{
strcpy(_str, s._str);
}
string& operator=(const string& s)
{
_size = s._size;
_capacity = s._capacity;
if (&s != this)
{
char* tmp = new char[s._capacity + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
}
return *this;
}
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity;
}
char operator[](size_t i) const
{
assert(i < _size);
return _str[i];
}
const char* c_str() const
{
return _str;
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* newcapa = new char[n + 1];
strcpy(newcapa, _str);
delete[] _str;
_str = newcapa;
_capacity = n;
}
}
void rsize(size_t n, char ch = '\0')
{
if (n < _size)
{
_str[n] = '\0';
_size = n;
}
else
{
if (n > _capacity)
{
reserve(n);
}
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_size = n;
_str[_size] = '\0';
}
}
//这里也可以和insert复用
void push_back(const char ch)
{
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
//这里也可以和insert复用
void append(const char* s)
{
size_t len = strlen(s);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, s);
_size += len;
}
void append(const string& s)
{
size_t len = s.size();
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, s._str);
_size += len;
}
string& operator+=(const char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* s)
{
append(s);
return *this;
}
string& operator+=(const string& s)
{
append(s);
return *this;
}
string& insert(size_t pos, char ch)
{
assert(pos < _size);
if (_size == _capacity)
{
size_t newcapa = _capacity == 0 ? 2 : _capacity * 2;
reserve(newcapa);
}
int end = _size;
while (end >= (int)pos)
{
_str[end + 1] = _str[end];
end--;
}
_str[pos] = ch;
_size++;
return *this;
}
string& insert(size_t pos, const char* s)
{
assert(pos < _size);
size_t len = strlen(s);
if (_size + len > _capacity)
{
reserve(_size + len);
}
int end = _size;
while (end >=(int)pos)//如果int小于0则与无符号比较会变成无符号。
{
_str[end + len] = _str[end];
end--;
}
//第一种方式
/*for (size_t i = 0; i < len; i++)
{
_str[pos++] = s[i];
}*/
//第二种方式
strncpy(_str + pos, s, len);
_size += len;
return *this;
}
string& erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
if (len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t i = pos + len;
while (i <= _size)
{
_str[pos++] = _str[i++];
}
_size -= len;
}
return *this;
}
size_t find(char ch , size_t pos = 0)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t find(const char* s, size_t pos = 0)
{
char* p = strstr(_str, s);
if (p == nullptr)
{
return npos;
}
return p - _str;
}
bool operator==(const string& s)
{
if (strcmp(_str, s._str) == 0)
{
return true;
}
return false;
}
bool operator<(const string& s)
{
if (strcmp(_str, s._str) < 0)
return true;
return false;
}
bool operator<=(const string& s)
{
return *this < s || *this == s;
}
bool operator>(const string& s)
{
return !(*this <= s);
}
bool operator>=(const string& s)
{
return !(*this < s);
}
bool operator!=(const string& s)
{
return !(*this == s);
}
private:
char* _str;
size_t _size;
size_t _capacity;
static size_t npos;
};
size_t string:: npos = -1;
ostream& operator<<(ostream& out, string& s)
{
for (size_t i = 0; i < s.size(); i++)
{
out << s[i];
}
return out;
}
istream& operator>>(istream& in, string& s)
{
while (1)
{
char ch;
ch = in.get();
if (ch == ' ' || ch == '\n')
{
break;
}
else
{
s += ch;
}
}
return in;
}
void test()
{
string s("hello");
string s1;
string s2(s);
string s3 = s;
string s4("world");
cin >> s1;
cout << s1 << endl;
}
}
以上则是相对完整的string实现,这里我们也可以进一步理解迭代器,string的迭代器是一个指针,利用指针进行遍历,而范围for则是依赖迭代器而形成的。
这里还有深拷贝现代写法的完整形式。
string(const string& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);
swap(tmp);
}
string& operator=(string s)
{
swap(s);
return *this;
}
void swap(string& s)
{
::swap(_str,s._str);
::swap(_size,s._size);
::swap(_capacity,s._capacity);
}
因为swap函数其作用域中,所以加域作用符来表明引用的是全局域的swap函数。