목차
I. 소개
이 글에서는 push_back, Reserve 및 기타 함수의 시뮬레이션을 포함하여 C++의 문자열 템플릿에서 일반적으로 사용되는 일부 함수를 시뮬레이션을 통해 구현합니다.그러나 모든 상황을 고려하지는 않으므로 함수 오버로드가 너무 많지는 않습니다. 끈의 무거운 짐).정말 너무 많아요...)
이 글은 두 부분으로 구성되어 있는데, 한 부분은 문자열을 구현하는 데 사용되며, 헤더 파일 이름은 string.h이고, 다른 부분은 소스 코드 구현 부분입니다(컴파일러 환경은 VS2019입니다). 각 함수의 반환값 유형과 매개변수 유형은 라이브러리의 문자열과 동일합니다.
2.string.h 헤더 파일
1. 클래스 구성
문자열 클래스에는 총 3개의 멤버가 있으며, _size는 길이, _capacity는 최대 용량, 마지막 멤버는 저장된 문자열 _str을 나타내는 데 사용됩니다. 구체적인 구현은 다음과 같습니다.
#pragma once
#include <assert.h>
namespace str
{
class string
{
public:
private:
size_t _size;
size_t _capacity;
char* _str;
}
};
2. 생성자와 소멸자
커스텀 클래스의 경우 생성자와 소멸자는 필수이므로 이 두 함수를 간단하게 구현해 보겠습니다.
①건설자
//构造函数(带缺省也可为默认构造)
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
//拷贝_size+1是因为后面还要跟个'\0'
memcpy(_str, str, _size + 1);
}
//深拷贝
string(const string& str)
{
_str = new char(str._capacity + 1);
_size = str._size;
_capacity = str._capacity;
memcpy(_str, str._str, _size + 1);
}
일반적으로 사용되는 생성자는 2가지가 있는데, 하나는 상수 문자열의 초기화이고, 다른 하나는 동일한 유형의 문자열 초기화인데, 동일한 유형의 문자열 초기화이므로 전체 복사(deep copy)가 필요합니다.
②소멸자
//析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
3.c_str 함수(c 문자열 가져오기)
이 함수는 간단하고 이해하기 쉽습니다. 멤버 문자열을 직접 반환하면 됩니다.
//c_str函数(返回C字符串)
const char* c_str() const
{
return _str;
}
4.empty 기능(비어있음 판단)
매우 간단합니다. 크기가 0인지 직접 확인합니다.
//empty函数(判断空)
bool empty() const
{
return _size == 0;
}
5.push_back 기능(꼬리 삽입)
요소를 삽입하는 가장 간단한 함수이지만 클래스에서는 항상 메모리 할당 크기에 주의해야 하므로 마지막에 데이터를 삽입할 때는 할당된 공간이 충분한지 확인해야 합니다.
//push_back函数(尾插)
void push_back(char ch)
{
if (_size == _capacity)
//2倍扩容
reserve(_capacity == 0 ? 4 : _capacity * 2);
_str[_size++] = ch;
_str[_size] = '\0';
}
여기서 사용하는 예비기능은 확장기능이며, 구체적인 확장크기는 실제 상황에 따라 조정될 수 있습니다.
6.예약 기능(확장)
예약 함수의 기능은 용량 확장만을 위한 기능으로, 함수 호출 시 클래스의 크기가 재정의된 크기보다 큰 경우 별도의 조치가 필요하지 않습니다. 작업을 수행하게 됩니다. 시간이 크게 늘어나므로 어떤 상황에서도 재구성하는 것은 권장되지 않습니다. 용량을 늘리면 됩니다. 구현은 다음과 같습니다.
//reserve函数(扩容)
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
memcpy(tmp, _str, _size + 1);
delete[] _str;
_capacity = n;
_str = tmp;
}
}
7.추가 기능(병합)
이 함수는 위의 push_back 구현 함수와 크게 다르지 않으며, 클래스 끝에 대상 문자열을 추가한다는 점만 다를 뿐이므로 여기서는 자세히 설명하지 않겠습니다.
//append函数(合并)
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
//至少扩容至刚好
reserve(_size + len);
}
memcpy(_str + _size, str, len + 1);
_size += len;
}
8. '+=' 오버로드
구현 및 라이브러리 함수 문자열 클래스에는 일반적으로 사용되는 += 두 가지가 있습니다: += 문자 및 += 문자열 구현 프로세스는 매우 간단합니다. 문자는 이전 push_back 함수를 직접 호출하고 문자열은 추가 함수를 호출합니다.
//重载'+='
string& operator+=(const char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
9. '=' 오버로드
Deep Copy가 필요하기 때문에 모두 오버로딩을 해야 합니다 = 원래 클래스 크기를 비워둔 후 Append 기능을 사용하여 클래스에 추가하면 됩니다.
//重载'='
string& operator=(const char* ch)
{
_size = 0;
_str[0] = '\0';
append(ch);
return *this;
}
10. '[]' 오버로드
[] 는 해당 하위 부분에 저장된 데이터를 구하는 것으로, 배열 문자열의 첨자를 구하는 것과 동일하며 직접 구하는 것이 가능하다.
//获取对应下标
char& operator[](size_t pos)
{
assert(_str[pos]);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(_str[pos]);
return _str[pos];
}
11.삽입 기능(삽입)
insert 함수에는 3개의 매개변수가 있는데, 자주 사용하는 문자와 문자열을 삽입하므로 이 함수를 오버로드해야 합니다. 구체적인 구현은 다음과 같으며, 필수 주의사항은 다음 코드에 표시되어 있습니다.
①문자 삽입
//insert函数(插入字符)
void insert(size_t pos,size_t n,char ch)
{
//排除下标越界
assert(pos <= _size);
//检查扩容
if (_size + n > _capacity)
{
reserve(_size + n);
}
//挪动数据
//此处防止整形提升,故使用size_t类型,并使用npos加以区分
size_t end = _size;
while (end >= pos && end != npos)
{
_str[end + n] = _str[end];
--end;
}
//插入数据
for (size_t i = 0; i < n; ++i)
{
_str[pos + i] = ch;
}
_size += n;
}
②문자열을 삽입하세요
//insert函数(插入字符串)
void insert(size_t pos, const char* str)
{
//排除下标越界
assert(pos <= _size);
//检查扩容
size_t len = strlen(str);
if (pos + len > _capacity)
{
reserve(pos + len);
}
//挪动数据
size_t end = _size;
while (end >= pos && end != npos)
{
_str[end + len] = _str[end];
end--;
}
//插入数据
for (int i = 0; i < len; ++i)
{
_str[pos + i] = str[i];
}
_size += len;
}
그 중 npos는 이 클래스의 네임스페이스에 정의한 정적 영역 부호 없는 정수형으로 문자열 클래스에서 소리가 나고 네임스페이스에 정의되며 크기는 "-1"입니다.
12.찾기 기능(찾기)
검색 기능은 삽입과 유사합니다. 검색은 문자와 문자열로 구분됩니다. 다른 매개변수는 시작할 위치입니다. 반환 값은 대상 첨자입니다. 구체적인 구현은 다음과 같습니다.
①캐릭터 찾기
//find函数(查找字符)
size_t find(char ch, size_t pos = 0)
{
assert(pos < _size);
size_t end = _size;
while (pos < end)
{
if (_str[pos] == ch)
return pos;
pos++;
}
return npos;
}
②문자열 찾기
//find函数(查找字符串)
size_t find(const char* str, size_t pos = 0)
{
size_t len = strlen(str);
assert(pos + len < _size);
//使用strstr比较字符串
const char* tmp = strstr(_str + pos, str);
if (tmp)
{
return tmp - _str;
}
return npos;
}
13.삭제 기능(삭제)
이 함수의 기능은 대상 첨자에서 지정된 양의 데이터를 삭제하는 것으로, 크기를 지정하지 않으면 기본적으로 저장된 데이터의 끝까지 삭제된다. 대상 첨자의 판단과 지정된 삭제 크기의 판단입니다.
//erase函数(删除)
void erase(size_t pos, size_t len = npos)
{
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t end = pos + len;
while (end <= _size)
{
_str[pos++] = _str[end++];
}
_size -= len;
}
}
14.크기 조정 기능(길이 재정의)
이 함수는 Reserve와 매우 유사하나 차이점은 Reserve의 기능은 용량 확장뿐이라는 점, resize는 용량을 확장할 수 있을 뿐만 아니라(물론 용량 축소는 불가능) 클래스 내 멤버 문자열의 길이를 업데이트할 수도 있다는 점이다. .
//resize函数(重定义长度)
void resize(size_t n, char s = '\0')
{
if (n >= _size)
{
reserve(n);
for (size_t i = _size; i < n; ++i)
{
_str[i] = s;
}
}
_size = n;
_str[n] = '\0';
}
15.substr 함수(하위 문자열 가져오기)
하위 문자열을 획득하려면 대상 첨자와 하위 문자열 크기라는 두 가지 매개변수가 필요합니다. 여기서 반환은 생성자의 전체 복사본을 사용합니다. 그렇지 않은 경우 오류가 직접 보고됩니다.
//substr函数(子字符串获取)
string substr(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
//特殊情况更改后一起处理
size_t n = len;
if (len == npos || len + pos > _size)
{
n = _size - pos;
}
string retun;
retun.reserve(n);
for (int i = pos; i < pos + n; ++i)
{
//此处使用+=更方便
retun += _str[i];
}
return retun;
}
16.스왑 기능(교환)
swap 함수 호출 시 라이브러리에서 문자열 swap 함수를 직접 호출하여 구현할 수 있으며, _size와 _capacity는 교환되지 않는다는 점 유의하시기 바랍니다.
//swap函数(交换)
void swap(string& str)
{
std::swap(_str, str._str);
size_t tmp = _size;
_size = str._size;
str._size = tmp;
tmp = _capacity;
_capacity = str._capacity;
str._capacity = tmp;
}
17.<<스트림 삽입 및>>스트림 추출
스트림 삽입과 스트림 추출에서 주의해야 할 두 가지 사항이 있습니다. 첫 번째는 둘 다 클래스 외부의 네임스페이스에 정의되어야 한다는 것이고, 두 번째로 두 가지 중 첫 번째 매개 변수 유형은 삽입 스트림의 별칭입니다. Zu Shiye 복사 방지 기능이 설정되었기 때문에 추출 스트림...구현은 다음과 같습니다.
①스트림 삽입
//流插入
ostream& operator<<(ostream& out, const str::string& str)
{
for (auto i : str)
{
out << i;
}
return out;
}
여기서 for 는 반복자를 사용하지만 우리가 직접 정의하지 않았으므로 다음 지점에서 설정하겠습니다.
②스트림 추출
스트림 추출은 스트림 삽입과 다르므로 용량 확장 문제를 고려해야 하며, 예약 함수를 계속해서 직접 호출할 수 있으나 운영 효율성이 정말 떨어지기 때문에 다음 확장이 최적화된 버전이다.
//流提取(定义在类外的命名空间内)
istream& operator>>(istream& in, str::string& str)
{
str.clear();
//使用get()防止无法写入' '
char ch = in.get();
while (ch == ' ' || ch == '\n')
{
ch = in.get();
}
int i = 0;
char tmp[128];
while (ch != ' ' && ch != '\n')
{
tmp[i++] = ch;
if (i == 127)
{
tmp[i] = '\0';
str += tmp;
i = 0;
}
ch = in.get();
}
if (i != 0)
{
tmp[i] = '\0';
str += tmp;
}
return in;
}
//清空数据(定义在类内)
void clear()
{
_str[0] = '\0';
_size = 0;
}
18. 반복자 설정
Iterator에 대해서는 별로 할 말이 없습니다. 초기 위치와 끝 위치만 설정하면 되지만, 이름은 Begin, End 등 원래 Alias와 동일해야 합니다. 그렇지 않으면 조상의 Iterator가 인식되지 않습니다~
//迭代器设置
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
19. 크기 비교
크기를 비교하려면 두 개의 함수 오버로드만 필요하며 나머지는 하나씩 도입할 수 있습니다. 비교는 다음과 같습니다.
//大小比较
bool operator<(const string& str) const
{
return _size < str._size && memcmp(_str, str._str, _size) < 0;
}
bool operator==(const string& str) const
{
return _size == str._size && memcmp(_str, str._str, _size) == 0;
}
bool operator>(const string& str) const
{
return !(*this < str || *this == str);
}
bool operator>=(const string& str) const
{
return !(*this < str);
}
bool operator<=(const string& str) const
{
return !(*this > str);
}
bool operator!=(const string& str) const
{
return !(*this == str);
}
3. 소스코드
#pragma once
#include <assert.h>
namespace str
{
class string
{
public:
//构造函数(带缺省也可为默认构造)
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
//strcpy(_str, str);
memcpy(_str, str, _size + 1);
}
//深拷贝
string(const string& str)
{
_str = new char(str._capacity + 1);
_size = str._size;
_capacity = str._capacity;
memcpy(_str, str._str, _size + 1);
}
//析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
//迭代器设置
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
//reserve函数(扩容)
void reserve(size_t n)
{
if (n > _capacity)
{
//new的[]是容量大小,()是初始化内容
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_capacity = n;
_str = tmp;
}
}
//push_back函数(尾插)
void push_back(char ch)
{
if (_size == _capacity)
//2倍扩容
reserve(_capacity == 0 ? 4 : _capacity * 2);
_str[_size++] = ch;
_str[_size] = '\0';
}
//append函数(合并)
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
//至少扩容至刚好
reserve(_size + len);
}
memcpy(_str + _size, str, len + 1);
_size += len;
}
//重载'='
string& operator=(const char* ch)
{
_size = 0;
_str[0] = '\0';
append(ch);
return *this;
}
//重载'+='
string& operator+=(const char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
//insert函数(插入)
void insert(size_t pos,size_t n,char ch)
{
//排除下标越界
assert(pos <= _size);
//检查扩容
if (_size + n > _capacity)
{
reserve(_size + n);
}
//挪动数据
//此处防止整形提升,故使用size_t类型,并使用npos加以区分
size_t end = _size;
while (end >= pos && end != npos)
{
_str[end + n] = _str[end];
--end;
}
//插入数据
for (size_t i = 0; i < n; ++i)
{
_str[pos + i] = ch;
}
_size += n;
}
void insert(size_t pos, const char* str)
{
//排除下标越界
assert(pos <= _size);
//检查扩容
size_t len = strlen(str);
if (pos + len > _capacity)
{
reserve(pos + len);
}
//挪动数据
size_t end = _size;
while (end >= pos && end != npos)
{
_str[end + len] = _str[end];
end--;
}
//插入数据
for (int i = 0; i < len; ++i)
{
_str[pos + i] = str[i];
}
_size += len;
}
//erase函数(删除)
void erase(size_t pos, size_t len = npos)
{
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t end = pos + len;
while (end <= _size)
{
_str[pos++] = _str[end++];
}
_size -= len;
}
}
//find函数(查找字符或字符串)
size_t find(char ch, size_t pos = 0)
{
assert(pos < _size);
size_t end = _size;
while (pos < end)
{
if (_str[pos] == ch)
return pos;
pos++;
}
return npos;
}
size_t find(const char* str, size_t pos = 0)
{
size_t len = strlen(str);
assert(pos + len < _size);
//使用strstr比较字符串
const char* tmp = strstr(_str + pos, str);
if (tmp)
{
return tmp - _str;
}
return npos;
}
//substr函数(子字符串获取)
string substr(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
//特殊情况更改后一起处理
size_t n = len;
if (len == npos || len + pos > _size)
{
n = _size - pos;
}
string retun;
retun.reserve(n);
for (int i = pos; i < pos + n; ++i)
{
//此处使用+=更方便
retun += _str[i];
}
return retun;
}
//resize函数(重定义长度)
void resize(size_t n, char s = '\0')
{
if (n >= _size)
{
reserve(n);
for (size_t i = _size; i < n; ++i)
{
_str[i] = s;
}
}
_size = n;
_str[n] = '\0';
}
//大小比较
bool operator<(const string& str) const
{
return _size < str._size && memcmp(_str, str._str, _size) < 0;
}
bool operator==(const string& str) const
{
return _size == str._size && memcmp(_str, str._str, _size) == 0;
}
bool operator>(const string& str) const
{
return !(*this < str || *this == str);
}
bool operator>=(const string& str) const
{
return !(*this < str);
}
bool operator<=(const string& str) const
{
return !(*this > str);
}
bool operator!=(const string& str) const
{
return !(*this == str);
}
//swap函数(交换)
void swap(string& str)
{
std::swap(_str, str._str);
size_t tmp = _size;
_size = str._size;
str._size = tmp;
tmp = _capacity;
_capacity = str._capacity;
str._capacity = tmp;
}
//empty函数(判断空)
bool empty() const
{
return _size == 0;
}
//清空数据
void clear()
{
_str[0] = '\0';
_size = 0;
}
//c_str函数(返回C字符串)
const char* c_str() const
{
return _str;
}
//获取对应下标
char& operator[](size_t pos)
{
assert(_str[pos]);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(_str[pos]);
return _str[pos];
}
private:
size_t _size;
size_t _capacity;
char* _str;
public:
const static size_t npos;
};
const size_t string::npos = -1;
};
//流插入
ostream& operator<<(ostream& out, const str::string& str)
{
for (auto i : str)
{
out << i;
}
return out;
}
//流提取
istream& operator>>(istream& in, str::string& str)
{
str.clear();
char ch = in.get();
while (ch == ' ' || ch == '\n')
{
ch = in.get();
}
int i = 0;
char tmp[128];
while (ch != ' ' && ch != '\n')
{
tmp[i++] = ch;
if (i == 127)
{
tmp[i] = '\0';
str += tmp;
i = 0;
}
ch = in.get();
}
if (i != 0)
{
tmp[i] = '\0';
str += tmp;
}
return in;
}