1、简单string类实现
class string
{
public:
//构造函数,无参和带参的
string()
:_str(nullptr)
{}
string(char* str)
:_str(new char[strlen(str) + 1])//在堆上多申请一个空间存\0
{
strcpy(_str, str);//再将数据拷贝到代码段
}
string(const string&s)//深拷贝
:_str(new char[strlen(s._str)+1])
{
strcpy(_str, s._str);
}
//全缺省
/*
string(char*str = "")//注意这里不可以给nullptr否则会出错,""只有一个\0
:_str(new char[strlen(str)+1])
{
strcpy(_str,str);
}
*/
//析构函数
~string()
{
delete[] _str;
_str = nullptr;
}
//size()
size_t size()
{
return strlen(_str);
}
//[]重载
char& operator[](size_t i)
{
return _str[i];
}
//赋值运算符重载
//考虑连等所以要有返回值
string operator=(const string& s)
{
if (this != &s)//防止自己给自己赋值
{
//新开空间,拷贝数据
char* tmp = new char[strlen(s._str) + 1];
strcpy(tmp, s._str);
//删除_str的原有空间
delete[]_str;
//让_str指向tmp
_str = tmp;
}
return *this;
}
//c_str()
const char* c_str()
{
return _str;
}
private:
char* _str;
};
void test_string1()
{
//拷贝构造(默认拷贝构造是浅拷贝)
string s1("lalalla");
string s2(s1);
//s1先析构s2再析构会把指针置空,所以会出错
string s3("wawawawaa");
s1 = s3; //会出错,因为 = 是浅拷贝
}
}
深拷贝的另外一种写法:(新)
string(consr string& s)
:_str(nullptr)
{
string tmp(s._str);
swap(_str,tmp._str);
swap(_size,tmp._size);
swap(_capacity,tmp._capacity);
}
string& operator=(const string& s)
{
if(this!=&s)
{
string tmp(s);
swap(_str,tmp._str);//tmp是个临时对象,所以后面会销毁
swap(_size,tmp._size);
swap(_capacity,tmp._capacity);
}
//简洁版
/*
string& operator=(string s)
//这里传值,s是拷贝构造出来的临时变量
{
swap(_str,s._str);
swap(_size,s._size);
swap(_capacity,s._capacity);
//互换_str和s._str的空间
return *this;
}
*/
}
2、完善具体string类
(1)构造函数
string(const char*str = "")
{
_size = strlen(str); //strlen计算长度遇到\0结束
_capacity = _size;
_str = new char[_capacity+1];//多开一个存/0
strcpy(_str, str);
}
//拷贝构造和赋值
string(const string& s)
{
_size = s._size;
_capacity = s._capacity;
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}
string& operator=(const string& s)
{
if (this != &s)
{
char*tmp = new char[s._capacity + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
1、若new一个char空间会有一个/0在里面
2、关于 strlen 与 sizeof 的区别:
(1) strlen 是函数,sizeof 是运算符。
(2)strlen 测量的是字符的实际长度,以’\0’ 结束(不包含’\0’ )。
而sizeof 测量的是字符的分配大小,如果未分配大小,则遇到’\0’ 结束
(包含’\0’ ,也就是strlen测量的长度加1),如果已经分配内存大小,返回的就是分配的内存大小。
3、C 库函数 char *strcpy(char *dest, const char *src) 把src所指向的字符串复制到 dest,深拷贝。
char s[] = { "hello world!" };
char s1[100] = { "hello world!" };
cout << strlen(s) << endl; //12
cout << sizeof(s) << endl; //13 有\0
cout << sizeof(s1) << endl; //100
(3)在子函数中,sizeof 会把从主函数中传进来的字符数组当作是指针来处理。 指针的大小又是由机器来决定,而不是人为的来决定的。
(4)sizeof是一个运算符,而strlen是一个函数,trlen的结果要运行的时候才能计算出来,而大部分编译程序在编译的的阶段就把sizeof计算过了是类型或者是变量的长度。
(2)析构
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
(3)size、capacity、c_str、[]重载
//size()
size_t size()const
{
return _size;
}
//capacity
size_t capacity()const
{
return _capacity;
}
//[]重载
char& operator[](size_t i)
{
assert(i < _size);
return _str[i];
}
const char& operator[](size_t i)const//const放在函数后面表示成员函数只读不可改
{
assert(i < _size);
return _str[i];
}
//c_str()
const char* c_str()
{
return _str;
}
(4)迭代器(string的迭代器是原生指针)
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
(5)reserve
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_capacity = n;
}
}
(6)push_back
//push_back
void push_back(char ch)
{
if (_size == _capacity)//考虑增容问题
{
size_t newcapacity = _capacity == 0 ? 2 : 2 * _capacity;//以二倍进行增容
reserve(newcapacity);
//char*tmp = new char[newcapacity + 1];//newcapacity是开的有效空间,得多开一个空间留给\0
//strcpy(tmp, _str);//将_str里面的内容拷贝到新空间tmp里面
//delete[] _str;//释放原空间
//_str = tmp;//指向新空间
//_capacity = newcapacity;
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';//没有\0会出错
}
(7)append和+=
//append
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
//这里没有以2倍增容,因为增容二倍不一定够
{
/*size_t newcapacity = _size + len;
char*tmp = new char[newcapacity + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = newcapacity;*/
reserve(_size + len);
}
strcpy(_str + _size, str);//将str拷贝到从str+size(\0的位置)开始的位置
_size += len;
}
//+=
string& operator+=(const char* str)
{
this->append(str);
return *this;
}
string& operator+=(char c)
{
this->push_back(c);
return *this;
}
(8)resize
void resize(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';
}
}
(9)insert
//insert
string& insert(size_t pos, char c)
{
assert(pos < _size);
if (_size == _capacity)//考虑增容
{
size_t newcapacity = _capacity == 0 ? 2 : 2 * _capacity;//以二倍进行增容
reserve(newcapacity);
}
int end = _size;
while (end >= pos)
{
_str[end+1] = _str[end];
--end;
if (end == -1)
{
break;
}
}
_str[pos] = c;
++_size;
return *this;
}
string& insert(size_t pos, const char* str)
{
assert(pos<_size);
//考虑增容
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
int end = _size;//数组最后是\0
while (end >= pos)
{
_str[end + len] = _str[end];
--end;
if (end == -1)
{
break;
}
}
/*for (size_t i = 0; i < len; i++)
{
_str[pos] = str[i];
pos++;
++_size;
}*/
strncpy(_str + pos, str, len);
_size += len;
return *this;
}
(10)erase
//erase
void erase(size_t pos, size_t len = npos)
{
if (len >= _size - pos)//string::npos在这里会是一个
{
_str[pos] = '\0';
}
else
{
size_t i = pos + len;
while (i<=_size)
{
_str[i - len] = _str[i];
++i;
}
}
_size -= len;
}
(11)find
//find
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* str, size_t pos = 0)
{
char*p = strstr(_str+pos, str);//在_str里查找str第一次出现的位置
if (p == nullptr)
{
return npos;
}
else
{
return p - _str;
}
}
//rfind
size_t rfind(char ch, size_t pos = npos)
{
if (pos != npos)
{
for (int i = pos; i >= 0; i--)
{
if (_str[i] == ch)
{
return i;
}
}
}
else if (pos==npos)
{
for (int i = _size; i >= 0; i--)
{
if (_str[i] == ch)
{
return i;
}
}
}
return npos;
}
char *strstr(const char *haystack, const char *needle) :
在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 ‘\0’
(12)大小比较
//大小比较
bool operator>(const string& s)
{
int ret = strcmp(_str, s._str);
return ret > 0;
}
bool operator<(const string& s)
{
int ret = strcmp(_str, s._str);
return ret < 0;
}
bool operator==(const string& s)
{
int ret = strcmp(_str, s._str);
return ret == 0;
}
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);
}
C语言 strcmp() 函数用于对两个字符串进行比较(区分大小写)。
头文件:string.h语法/原型:
int strcmp(const char* stri1,const char* str2); 参数 str1 和 str2
是参与比较的两个字符串。strcmp() 会根据 ASCII 编码依次比较 str1 和 str2
的每一个字符,直到出现不到的字符,或者到达字符串末尾(遇见\0)。返回值:
如果返回值 < 0,则表示 str1 小于 str2。 如果返回值 > 0,则表示 str2 小于 str1。 如果返回值 = 0,则表示 str1 等于 str2。
(14)输入输出运算符重载
std::istream& operator>>(std::istream& in, string& s)
{
while (1)
{
char ch;
ch=in.get();//ch=in.getline()
if (ch == ' ' || ch == '\n')
{
break;
}
else
{
s += ch;//包含扩容问题
}
}
return in;
}
std::ostream& operator<<(std::ostream& out, const string& s)
{
for (int i = 0; i < s.size(); ++i)
{
std::cout << s[i];
}
return out;
}
整体代码链接:
(https://github.com/Wilingpz/sunny/tree/master/%E6%A8%A1%E6%8B%9F%E5%AE%9E%E7%8E%B0string