//测试:遍历用来测试代码:
void print_str()
{
assert(_str != nullptr);
for (size_t pos = 0; pos < _size; pos++)
{
std::cout << _str[pos];
}
std::cout << std::endl;
}
1.我们观察一下实现1和4可以实现一个缺陷省参数去把两个构造给包含在一起:
//1-1:空和const char* s
string(const char* str="\0")//const char* str = "" C语言特性空字符串就是一个"\0"
{
//问题一:建议不要使用初始化列表进行:因为初始化列表的顺序是根据成员变量声明的顺序。
//问题二:应该开辟的空间大小?
//_size 的意义是:当前有效字符的个数 _capacity的意义是:当前可以存放的最多的有效字符的个数:
if (str == "\0")
{
_str = new char[1] {'\0'};
_size = 0;
_capacity = 0;
}
else
{
size_t len = strlen(str);
_str = new char[len+1];
strcpy(_str, str);
_size = len;
_capacity = len;
}
}
2.2是我们的拷贝构造是需要实现的!
1.传统写法:自己去开辟空间自己完成这个拷贝
string(const string& str)
{
if (str._size == str._capacity)
{
if (str._capacity == 0)
{
_str = new char[1] {'\0'};
_size = 0;
_capacity = 0;
}
else
{
_str = new char[str._capacity + 1];
for (int i = 0; i < str._size; i++)
{
_str[i] = str._str[i];
}
_size = str._size;
_capacity = str._capacity;
}
}
}
2.现代写法:调用构造函数并且交换实现这个构造函数!
//1-2-1:拷贝构造(现代写法):
string(string& str)
{
string s1(str._str);
(*this).swap(s1);
}
3.6是使用n个C字符实现去构造一个字符串也可以实现一下:
string(size_t n, const char c)
{
if (n == 0)
{
_str = new char[1] {'\0'};
_size = 0;
_capacity = 0;
}
else
{
_str = new char[n + 1];
size_t pos = 0;
for (pos = 0; pos < n; pos++)
{
_str[pos] = 'c';
}
_str[pos] = '\0';
_size = n;
_capacity = n;
}
}
//2:析构函数:
~string()
{
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
1.string 类型的赋值:(传统写法)
//2-1:字符串赋值:
string& operator=(const string& str)
{
//1.排除自己给自己赋值的情况!
if (this == &str)
return *this;
//2.考虑str比较长的情况:
if (str._size > _capacity)
{
//1.开一个空间:
char* tmp = new char[str._size + 1];
//2.拷贝数据到新空间:
strcpy(tmp, str._str);
//3.释放旧空间:
delete[] _str;
_str = tmp;
_size = str._size;
_capacity = str._size;
}
//3.被赋值的变量的_capacity是足够的!
else
{
strcpy(_str, str._str);
_size = str._size;
}
return *this;
}
1-1.string 类型的赋值:(现代写法)
//2-2:字符串赋值(现代赋值):
string& operator=(const string& str)
{
string s1(str._str);
swap(s1);
return *this;
}
2.const char* 类型的赋值:(传统写法)
//2-2:const char* 赋值:
string& operator=(const char* str)
{
size_t len = strlen(str);
//2.考虑str比较长的情况:
if (len > _capacity)
{
//1.开一个空间:
char* tmp = new char[len + 1];
//2.拷贝数据到新空间:
strcpy(tmp, str);
//3.释放旧空间:
delete[] _str;
_str = tmp;
_size = len;
_capacity = len;
}
//3.被赋值的变量的_capacity是足够的!
else
{
strcpy(_str, str);
_size = len;
}
return *this;
}
//3-1:size():不对对象进行修改只去读数据的代码给*this加一个const修饰!
size_t size()const
{
return _size;
}
//3-2: capacity()
size_t capacity()const
{
return _capacity;
}
//3-3:reserve():n为多大就开多大的有效空间:
void reserve(size_t n)
{
//1.不考虑容量减少的情况:
//2.C++ 下是没有realloc函数:
//2-1:开一个大小为n的新的空间:
char* tmp = new char[n+1];
//2-2:把字符串用来的内容放到新开辟好的空间里面:
strcpy(tmp, _str);
//2-3:原来空间释放+数值的更新:
delete[] _str;
_str = tmp;
_capacity = n;
}
//3-4:clear():擦除字符串内容+把字符串变成空字符串
void clear()
{
delete[] _str;
_str = new char[1] {'\0'};
_size = 0;
_capacity = 0;
}
//3-5:empty():判断字符串是否为空:
bool empty()
{
if (_size == 0)
{
return true;
}
return false;
}
//4-1:c_str():
const char* c_str()const
{
return _str;
}
//4-2:operator[]
//4-2-1:只读:
//const string * this 修饰内容:
//1.引用返回造就了可读可写!
//2.双const修饰防止了引用返回导致的修改的可能性!
const char& operator[](size_t pos)const
{
assert(_str != nullptr);
return _str[pos];
}
//4-2-2:可读可写:
//1.引用返回造就了可读可写!
char& operator[](size_t pos)
{
assert(_str != nullptr);
return _str[pos];
}
//5-1:push_back()
void push_back(char c)
{
assert(_str != nullptr);
if (_size == _capacity)
{
//1.处理开始为0的情况,正常就去扩二倍:
_capacity = _capacity == 0 ? 4 : _capacity*2;
//2.使用reserve非常方便:
reserve(_capacity);
}
//3.要保持住结尾的'\0'
_str[_size++] = c;
_str[_size] = '\0';
}
1.加上一个字符串:
//5-2-1:添加一个字符串:
string& append(const string& str)
{
assert(_str != nullptr);
size_t len = str.size();
if (len + _size > _capacity)
{
reserve(len + _size + 1);
}
strcpy(_str+_size, str._str);
_size += str._size;
return *this;
}
2.加上一个const char*
//5-2-2:添加一个const char* 的值:
string& append(const char* str)
{
assert(_str != nullptr);
size_t len = strlen(str);
if (len + _size > _capacity)ize
{
reserve(len + _size + 1);
}
strcpy(_str + _size, str);
_size += len;
return *this;
}
string& operator+= (const string& str)
{
append(str);
return *this;
}
string& operator+= (char c)
{
push_back(c);
return *this;
}
string& operator+= (const char* s)
{
append(s);
return *this;
}
string& insert(size_t pos , char c)
{
assert(pos < _size);
if (_size + 1 > _capacity)
{
reserve(_size + 1);
}
size_t end = _size + 1;
while (pos < end)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = c;
_size += 1;
_str[_size] = '\0';
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);
}
size_t end = _size + len;
while (pos < end)
{
_str[end] = _str[end - len];
end--;
}
strncpy(_str + pos, str,len);
_size += len;
_str[_size] = '\0';
return *this;
}
string& erase(size_t pos = 0, size_t len = npos)
{
assert(_str != nullptr);
if (len == npos || len > _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
}
return *this;
}
1.swap 为什么会有三个重载:
2.string 类实现的swap方法:
3.swap一个全局的:
4.使用全局函数模板生成的一个string
void swap(string& s1, string& s2)
{
char* tmp = s1._str;
size_t tmp_1 = s1._size;
size_t tmp_2 = s2._capacity;
s1._str= s2._str;
s1._size= s2._size;
s1._capacity= s2._capacity;
s2._str = tmp;
s2._size = tmp_1;
s2._capacity = tmp_2;
}
//6-3:swap()
void swap(string& str)
{
swap(*this, str);
}
size_t find(const char* ch, size_t pos = 0)
{
assert(pos < _size);
//1.有可能找不到子串:
const char* ptr = strstr(_str, ch);
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - _str;
}
}
//7:find() substr()
size_t pos(char ch , size_t pos = 0)
{
assert(pos < _size);
for (size_t pos = 0; pos < _size; pos++)
{
if (_str[pos] == ch)
{
return pos;
}
}
}
string substr(size_t pos , size_t len=npos)
{
string str;
if (len == npos || len > _size-pos)
{
str.reserve(_size - pos);
}
for (size_t i = pos; i < _size; i++)
{
str += _str[i];
}
return str;
}
1.begin()
//8:iterator()
typedef char* iterator;
iterator begin()
{
return _str;
}
2.end()
iterator end()
{
return _str + _size;
}
3.使用:
void text_8()
{
sfpy::string s1("hello iterator");
sfpy::string::iterator it_1 = s1.begin();
while (it_1!=s1.end())
{
std::cout << (*it_1);
it_1++;
}
}
1.使用scanf获取标准输入(stdin)中的数据。
2.C语言中的标准输入和C++ 的标准输入是不一样的!
3.C++ 强制去兼容C语言在这个地方!
4.这个地方获取一个字符串可以获取到空格的版本!
5.获取数据之前是需要清空这个空间因为我们是一个赋值!
std::istream& operator>>(std::istream& _cin, string& s1)
{
//assert(string::_str != nullptr);
s1.clear();
char tmp = 0;
while (scanf("%c", &tmp) != 0 && tmp != '\n')
{
s1.push_back(tmp);
}
return _cin;
}
方法二:优化1
1.这里我们可以去使用C++ 的输入!
2.关于 cin>>ch 其实是获取不了空格和\n
3.获取不了\n和空格是结束不了这一次的字符串输入:
4.在cin中有一个方法get() 方法!
std::istream& operator>>(std::istream& _cin, string& s1)
{
//assert(string::_str != nullptr)
s1.clear();
//从缓冲区一个一个读从缓冲区到文件中!
char tmp = _cin.get();
while (tmp != ' ' && tmp != '\n')
{
s1 += tmp;
tmp = _cin.get();
}
return _cin;
}
方法二:优化2
1.在这个地方我们使用operator+=(operator+= 底层使用判断空间大小和进行扩容每次扩容二倍) 。
2.如果输入一个比较长的字符串那么肯定会存在多次扩容降低了效率!
3.如果开始就开比较大的空间那么如果需要的空间并不大那么空间浪费如果字符非常多相当于这样的开始开比较大的空间的用处也不是很大!
4.在函数中定义一个字符数组确定一个大小输入数据不直接往字符串中输入而是先输入到字符数组中!
std::istream& operator>>(std::istream& _cin, string& s1)
{
//assert(string::_str != nullptr)
s1.clear();
char arr[128] = {0};
int i = 0;
//从缓冲区一个一个读从缓冲区到文件中!
arr[i] = _cin.get();
while ( arr[i]!= ' ' && arr[i] != '\n')
{
i++;
arr[i] = _cin.get();
if (i == 126)
{
arr[125] = '\0';
s1 += arr;
i = -1;
}
}
if (i>=0)
{
arr[i] = '\0';
s1 += arr;
}
return _cin;
}
std::ostream& operator<<(std::ostream& _cout, string& s1)
{
//assert(string::_str != nullptr);
string::iterator it_1 = s1.begin();
while (it_1 != s1.end())
{
_cout << (*it_1);
it_1++;
}
return _cout;
}