string的模拟实现
string.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace yjc
{
class string
{
public:
//typedef char* iterator; // 实现迭代器, 原来迭代器就是一个像指针的东西,任何容器都是
using iterator = char*; // 因为string底层的物理结构是连续的,所以直接用指针就可以实现
using const_iterator = const char*;
//string();
//string(const char* str);
string(const char* str = "");
string(const string& s);
string& operator= (const string& s);
~string();
void reserve(size_t n);
void push_back(char ch);
void append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);
void insert(size_t pos, char ch);
void insert(size_t pos, const char* str);
void erase(size_t pos, size_t len = npos);
size_t find(char ch, size_t pos = 0);
size_t find(const char* str, size_t pos = 0);
string substr(size_t pos, size_t len = npos);
void clear()
{
_str[0] = '\0';
_size = 0;
}
char& operator[](size_t i) // 可读可写
{
assert(i < _size);
return _str[i];
}
const char& operator[](size_t i) const
{
assert(i < _size);
return _str[i];
}
size_t size() const
{
return _size;
}
iterator begin() // 迭代器要结合begin和end一起使用
{
return _str;
}
iterator end()
{
return _str + _size; // 指向的是最后一个位置的下一个位置
}
const_iterator begin() const // 实现一个const修饰的迭代器 注意这里的命名,表示不是迭代器不能修改,而是迭代器指向的内容不能修改
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
const char* c_str() const// 隐含的this指针好用
{
return _str;
}
void swap(string& s);
private:
char* _str;
size_t _size;
size_t _capacity;
public:
// static const size_t npos = -1; // 静态成员变量按理来说是类中声明类外定义,但是有const修饰 之后就可在类中定义,而且只有整形才可以, 是一个特殊处理
static const size_t npos;
};
void swap(string& s1, string& s2);
// 全部重载为全局函数,为了支持字符串和string比较,类中的成员函数有抢位置的问题
bool operator== (const string& lhs, const string& rhs); // 常量字符串可以隐式类型转换为string类型
bool operator!= (const string& lhs, const string& rhs);
bool operator> (const string& lhs, const string& rhs);
bool operator< (const string& lhs, const string& rhs);
bool operator>= (const string& lhs, const string& rhs);
bool operator<= (const string& lhs, const string& rhs);
ostream& operator<<(ostream& os, const string& str);
istream& operator>>(istream& is, string& str);
istream& getline(istream& is, string& str, char delim = '\n');
}
string.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
namespace yjc // 定义多个命名空间,名字相同的会默认合并
{
const size_t string::npos = -1; // 走声明和定义分离
void string::reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1]; // 多开一个空间给\0
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void string::push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0? 4: 2 * _capacity); // 使用三目表达式,因为有可能空间是0
}
_str[_size] = ch;
_size++;
}
void string::append(const char* str)
{
size_t len = strlen(str);
//size_t newCapacity = _capacity * 2;
if (_capacity < _size + len)
reserve(_size + len);
strcpy(_str + _size, str);
_size += len;
}
string& string::operator+=(char ch)
{
push_back(ch);
return *this;
}
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
void string::insert(size_t pos, char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity); // 使用三目表达式,因为有可能空间是0
}
// 方法一
//int end = _size;
//while (end >= (int)pos) // 注意这里存在整形提升,会导致程序崩溃,所以必须强转int
//{
// /*if (end == 0)
// {
// int i = 0;
// }*/ // 手动写一个条件断点,方便调试
// _str[end + 1] = _str[end];
// end--;
//}
// 方法二
size_t end = _size + 1;
while (end > _size)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch;
_size++;
}
void string::insert(size_t pos, const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
size_t newCapacity = 2 * _capacity;
if (_size + len > newCapacity)
{
newCapacity = _size + len;
}
reserve(newCapacity);
}
//int end = _size;
//while (end >= (int)pos) // 防止整型提升
//{
// _str[end + len] = _str[end];
// --end;
//}
size_t end = _size + len;
while (end > pos +len -1)
{
_str[end] = _str[end - len];
end--;
}
for (int i = 0; i < len; i++)
{
_str[pos + i] = str[i];
}
_size += len;
}
void string::erase(size_t pos, size_t len)
{
assert(pos < _size);
if (len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t end = pos + len;
while (end <= _size)
{
_str[end - len] = _str[end];
++end;
}
_size -= len;
}
}
size_t string::find(char ch, size_t pos)
{
assert(pos < _size);
for (int i = pos; i < _size; i++)
{
if (_str[i] == ch)
return i;
}
return npos;
}
size_t string::find(const char* str, size_t pos)
{
assert(pos < _size);
const char* ptr = strstr(_str + pos, str); // 直接调用库函数,是一个暴力匹配的思想,返回第一个匹配的位置
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - _str; // 返回值是串中的n个元素
}
}
string string::substr(size_t pos, size_t len)
{
assert(pos < len);
if (len > _size - pos)
{
len = _size - pos;
}
yjc::string sub;
sub.reserve(len);
for (size_t i = 0; i < len; i++)
{
sub += _str[pos + i];
}
return sub; // 这边必须传值返回,调用拷贝构造
}
//string::string()
// :_str(new char[1] {'\0'}) // 这里注意不能直接给一个空指针,否则程序会崩溃,并且要用数组初始化,为了和析构保持一致
// , _size(0)
// , _capacity(0)
//{}
//string::string(const char* str)
// :_size(strlen(str))// 先开空间,再初始化,注意要+1 strlen不计算 \0 的空间
//{
// // 只让_size走初始化列表,避免多次使用strlen,并且不用管定义的顺序
// _str = new char[_size + 1];
// _capacity = _size;
// strcpy(_str, str);
//}
string::string(const char* str)// 直接给一个全缺省的构造函数, 要指定类域
:_size(strlen(str))
{
_str = new char[_size + 1];
_capacity = _size;
strcpy(_str, str);
}
//传统写法
//string::string(const string& s)
//{
// _str = new char[s._capacity + 1]; // 完成深拷贝
// strcpy(_str, s._str);
// _size = s._size;
// _capacity = s._capacity;
//}
//现代写法
string::string(const string& s)
{
string tmp(s._str); // 调用拷贝构造
swap(tmp); // 复用swap
}
//string& string::operator= (const string& s)
//{
// if (this != &s) // 防止自己给自己赋值
// {
// delete[] _str;
// _str = new char[s._capacity + 1];
// strcpy(_str, s._str);
// _size = s._size;
// _capacity = s._capacity;
// return *this; // 有连续赋值的情况,所以要有返回值
// }
//}
//现代写法
string& string::operator= (const string& s)
{
if (this != &s)
{
string tmp(s._str);
swap(tmp);
}
return *this;
}
string::~string()
{
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
void string::swap(string& s)
{
std::swap(_str, s._str); // 直接调用算法库的swap使其改变指针指向就行
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
void swap(string& s1, string& s2)
{
s1.swap(s2); // 又实现一个全局swap为了实现和算法库的统一,这里的swap相当于是对string的swap的一个封装
// 一共有三个swap,一个算法库的,一个string的,还有一个对string的封装的
}
bool operator== (const string& lhs, const string& rhs)
{
return strcmp(lhs.c_str(), rhs.c_str()) == 0;
}
bool operator!= (const string& lhs, const string& rhs)
{
return !(lhs == rhs);
}
bool operator> (const string& lhs, const string& rhs)
{
return !(lhs <= rhs);
}
bool operator< (const string& lhs, const string& rhs)
{
return strcmp(lhs.c_str(), rhs.c_str()) < 0;
}
bool operator>= (const string& lhs, const string& rhs)
{
return !(lhs < rhs);
}
bool operator<= (const string& lhs, const string& rhs)
{
return (lhs < rhs || lhs == rhs);
}
ostream& operator<<(ostream& os, const string& str)
{
for (int i = 0; i < str.size(); i++)
{
os << str[i];
}
return os;
}
istream& operator>>(istream& is, string& str)
{
str.clear();
//方法一
//char ch;
is >> ch; // 流提取操作符的默认行为是忽略空格和换行
//ch = is.get(); // get函数会读空格和换行
//方法二
int i = 0;
char buff[256]; // 在栈上开空间
char ch = is.get();
while (ch != ' ' && ch != '\n')
{
//str += ch;
buff[i++] += ch;
if (i == 255)
{
buff[i] = '\0';
str += buff; // 一次扩容
i = 0;
}
ch = is.get();
}
if (i > 0)
{
buff[i] = '\0';
str += buff;
}
return is;
}
istream& getline(istream& is, string& str, char delim)
{
str.clear();
int i = 0;
char buff[256];
char ch = is.get();
while (ch != delim)
{
buff[i++] += ch;
if (i == 255)
{
buff[i] = '\0';
str += buff; // 一次扩容
i = 0;
}
ch = is.get();
}
if (i > 0)
{
buff[i] = '\0';
str += buff;
}
return is;
}
}
test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
// 构造 遍历
void Test1()
{
//yjc::string s1 = "hello world"; // 要加上命名空间,不然会默认先去库里面找
//cout << s1.c_str() << endl;
//s1[0] = 'x';
//for (int i = 0; i < s1.size(); i++)
//{
// cout << s1[i];
//}
yjc::string s2 = "hello world";
cout << s2.c_str() << endl;
yjc::string::iterator it = s2.begin();
//while (it != s2.end())
//{
// cout << *it;
// it++;
//}
while (it != s2.end())
{
(*it)--;
cout << *it;
it++;
}
cout << endl;
for (auto& ch : s2) // 注意这边auto只能识别char,想用引用要自己加, 引用可以减少拷贝
{
ch++;
}
for (auto ch : s2) // 实现了迭代器 就能支持范围for,并且要咱找stl的命名规则命名迭代器
{
cout << ch;
}
cout << endl;
const yjc::string s3 = "xxxxxxxx";
for (auto& ch : s3)
{
// ch++;
cout << ch;
}
}
// 插入
void Test2()
{
yjc::string s1 = "hello world";
cout << s1.c_str() << endl;
s1 += 'x';
s1 += "6666";
cout << s1.c_str() << endl;
yjc::string s2 = "hello world";
s2.insert(0, 'x');
cout << s2.c_str() << endl;
s2.insert(0, "yjcyjcyjcyjc");
cout << s2.c_str() << endl;
}
void Test3()
{
yjc::string s1 = "hello world";
s1.erase(6, 2);
cout << s1.c_str() << endl;
}
void Test4()
{
yjc::string s1 = "hello world";
cout << s1.find(' ') << endl;
cout << s1.find("wor") << endl;
}
void Test5()
{
yjc::string url = "https://zhrz.nuc.edu.cn/cas/login?service=https%3A%2F%2Fzhmh.nuc.edu.cn%2Fpersonal-center";
size_t pos1 = url.find(':');
size_t pos2 = url.find('/', pos1 + 3);
if (pos1 != string::npos && pos2 != string::npos)
{
yjc::string domain = url.substr(pos1 + 3, pos2 - pos1 - 3); // 获取域名
cout << domain.c_str() << endl;
}
}
void Test6()
{
yjc::string s1;
//cin >> s1;
//cout << s1;
getline(cin, s1, '#');
cout << s1;
}
int main()
{
Test6();
return 0;
}