[C++] 문자열 템플릿을 구현하는 간단한 시뮬레이션

목차

I. 소개

2.string.h 헤더 파일

1. 클래스 구성

2. 생성자와 소멸자

①건설자

②소멸자

3.c_str 함수(c 문자열 가져오기)

4.empty 기능(비어있음 판단)

5.push_back 기능(꼬리 삽입)

6.예약 기능(확장)

 7.추가 기능(병합)

8. '+=' 오버로드

9. '=' 오버로드

10. '[]' 오버로드

11.삽입 기능(삽입)

①문자 삽입

②문자열을 삽입하세요

12.찾기 기능(찾기)

①캐릭터 찾기

②문자열 찾기

13.삭제 기능(삭제)

14.크기 조정 기능(길이 재정의)

15.substr 함수(하위 문자열 가져오기)

16.스왑 기능(교환)

17.<<스트림 삽입 및>>스트림 추출

①스트림 삽입

②스트림 추출

18. 반복자 설정

19. 크기 비교

 3. 소스코드


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;
}

추천

출처blog.csdn.net/qq_74641564/article/details/131666872