BST 문자열 입력

Yama·2024년 1월 11일
0

어소트락 수업

목록 보기
40/55

문자열이 같다(==)는걸 비교할떄 필요한 맴버함수

	// 1
	m_pStr;			_Other.m_pStr;
    // 2
	m_MaxLength;	_Other.m_MaxLength;
    // 3
    m_Length; _Other.m_Length;
  • 1번은 == 비교할떄 문자열의 주소비교는 필요가 없다.
    • 주소가 같을 수가 없다
    • 하나는 힙메모리 하나는 ROM에 저장되서
  • 2번은 최대 길이또한 필요하지 않다.
  • 3번의 문자열의 길이는 필요하다.
    • 문자열이 같다면 길이는 비교하는 문자열끼리는 길이가 일치해야한다.

문자열 operator == 구현

bool Str::operator==(const Str& _Other) const
{
	// 1
	if (m_Length != _Other.m_Length)
	{
		return false;
	}
    // 2
	for (int i = 0; i < m_Length; ++i)
	{
		// 3
		if (m_pStr[i] != _Other.m_pStr[i])
		{
			return false;
		}
	}

	// 4
	return true;
}
  • 1번은 각 개체의 문자열의 길이가 동일하지 않으면 같지 않다.
  • 2번은 모든 문자가 같은지 비교하는 for문.
  • 3번은 중간에 하나라도 다르면 두 문자열은 같지 않다.
  • 4번은 문자열의 길이가 같고, 모든 문자가 동일하면 두 문자열은 같은 문자열이다.

문자열 operator != 구현

bool Str::operator!=(const Str& _Other)const
{
	return !((*this) == _Other);
}
  • 방금 비교한 결과(==)를 받아서 거꾸로 주면 된다.

문자열의 대소 비교.

  • 문자열이 길이가 다를때 문자열 같은 구간이 먼저 끝난애가 앞선다.(도서관 책)
aa << ab
aa`x`(없다) < aab
aa > x(없다) 

operator < 구현

bool Str::operator<(const Str& _Other)const
{
	// 1
	assert(m_Length || _Other.m_Length);

	// 2
	int MinLen = m_Length < _Other.m_Length ? m_Length : _Other.m_Length;

	// 3
	for (int i = 0; i < MinLen; ++i)
	{
		if (m_pStr[i] < _Other.m_pStr[i])
			return true;
		else if (m_pStr[i] > _Other.m_pStr[i])
			return false;
	}

	// 4
	if (m_Length < _Other.m_Length)
		return true;
	else
		// 5
		return false;
}
  • 1번은 비교하려는 두 문자열이 모두 문자가 하나도 없다면 비교할 수가 없으므로 assert
  • 2번은 삼항연산자로 문자열 길이가 다른걸 비교할떄 반복문 도는걸 짧은걸로 기준을 잡기위한 int변수를 잡아서 넣는다.
    • 3번은 문자열 길이 다를떄 작은 값까지 반복문을 돈다.
  • 4번은 모든 문자가 동일했다면 문자열이 길이가 더 짧은 문자열이 더 작다.
    • 여기까지 반환값을 못받고 왔다면 문자열 길이는 다르지만 작은값에 길이까지는 두 문자열이 같다는 소리다.
      • abc, abccccc
  • 5번은 길이는 같은 경우도 <기 떄문에 false다.

operator > 구현

bool Str::operator>(const Str& _Other)const
{
	assert(m_Length || _Other.m_Length);

	int MinLen = m_Length < _Other.m_Length ? m_Length : _Other.m_Length;

	for (int i = 0; i < MinLen; ++i)
	{
		if (m_pStr[i] > _Other.m_pStr[i])
			return true;
		else if (m_pStr[i] < _Other.m_pStr[i])
			return false;
	}

	if (m_Length > _Other.m_Length)
		return true;
	else
		return false;
}
  • <의 결과를 뒤집으면 안되고 케이스들을 반대로 돌려야된다.
    • ==가 섞여 있기 떄문이다.

<> 테스트

	Str str1(L"Father");
    // 2 // 6
	Str str2(L"Mother");
    // 3 // 7
	Str str2(L"Father");
    // 4 // 8
	Str str2(L"Fatherr");
	
    // 1
	bool b = str1 < str2;
    // 5
    bool b = str1 < str2;
  • 1번
    • 2번 케이스는 true 반환
      • 첫글자에서 우열이 갈린다.
    • 3번케이스는 false 반환!
    • 4번케이스 true 반환
  • 5번
    • 6번 false 반환
    • 7번 false 반환
    • 8번 false 반환.

operator <= 구현

bool Str::operator<=(const Str& _Other)const
{
	// 1
	if (!(m_Length || _Other.m_Length))
		return true;
        
	int MinLen = m_Length < _Other.m_Length ? m_Length : _Other.m_Length;

	// 2
	for (int i = 0; i </*2<=*/ MinLen; ++i)
	{
		if (m_pStr[i] < _Other.m_pStr[i])
			return true;
		else if (m_pStr[i] > _Other.m_pStr[i])
			return false;
	}

	// 3 // 4
	if (m_Length < _Other.m_Length)
		return true;
	else if (m_Length > _Other.m_Length)
		return false;
    // 5
	else
		return true;
	// 6
	if (m_Length <= _Other.m_Length)
		return true;
	else
		return false;
}
  • 1번은 둘다 0,0여도 같다라고 판정 가능
    • 두 문자열이 0,0이면 거짓이 나오는데 그걸 !해서 true로 바꿔주면 그 경우만 조건문이 true를 반환하게한다.
  • 2번은 <=를 쓰면 안된다.
    • abcabd를 비교할떄는 aa를 첫번쨰에 비교를 해버리는 순간 두 문자열은 같지않은데 true가 떠버려서 쓰면 안된다.
  • 3번은 모든 문자가 동일했다면 문자열이 길이가 더 짧은 문자열이 더 작다.
  • 5번은 if와 else if 둘다 안걸렸다면 같다는 소리
  • 6번은 (4번+5번)의 축약 버전이다.

operator >= 구현

bool Str::operator>=(const Str& _Other)const
{
	if (!(m_Length || _Other.m_Length))
		return true;

	int MinLen = m_Length < _Other.m_Length ? m_Length : _Other.m_Length;

	for (int i = 0; i < MinLen; ++i)
	{
		if (m_pStr[i] > _Other.m_pStr[i])
			return true;
		else if (m_pStr[i] < _Other.m_pStr[i])
			return false;
	}

	if (m_Length >= _Other.m_Length)
		return true;
	else
		return false;
}
  • <= 대소비교 뒤집어서 구현하면 끝.

<=>= 테스트

	Str str1(L"Father");
	Str str2(L"Father");
	bool b = str1 <= str2;
	bool b = str1 >= str2;
  • true, true!

표준라이브러리 map은 되고 우리가 구현한 BST는 안되는것

	// 1
	BST<Str, int> mybst;
	// 2
	Str mystr(L"Test");
    // 3
	mybst.insert(make_bstpair(mystr, 0));
	// 4
	mybst.insert(make_bstpair(L"Test", 0));
  • 입력을 T1&로 받았었다.
  • 1번은 우리가 만든 이진탐색트리(BST)에 우리가 만든 문자열 객체를 넣었다.
  • 2번에서 문자열 Test를 담은 변수 mystr만든다.
  • 3번은 Str객체인 mystr은 잘들어간다.
  • 4번은 문자열을 바로 집어 넣으면 오류가 난다.
	BSTPair<T1, T2> make_bstpair(const T1& _first, const T2& _second)
  • 우리가 만든 make_bstpair함수는 BST만들때 T1타입을 Str로 지정했다.
	void BST<T1, T2>::insert(const BSTPair<T1, T2>& _pair)
  • 그리고 insert함수는 T1으로 구성되어 있는 _pair를 요구함.
	make_bstpair(L"Test", 0);
  • <Str, int>를 안적어주면 컴파일러는 인식을 사진처럼한다
	make_bstpair<Str, int>(L"Test", 0);
  • <Str, int>를 적어주면 컴파일러는 우리가 원하는 형식으로 인식한다
    • 우리가 만든 make_bstpair는 이것을 전달하고싶다.
	mybst.insert(make_bstpair(L"Test", 0));
  • <Str, int>를 안적어주면 컴파일러는 인식을 사진처럼한다
	mybst.insert(make_bstpair<Str, int>(L"Test", 0));
  • <Str, int>를 적어주면 컴파일러는 우리가 원하는 형식으로 인식한다
    • 4번은 실제로 <Str, int>가 적혀있어야 오류가 안뜬다.
  • 근데 우리는 <Str, int>를 입력안해도 우리가 원하는 Str형식으로 들어갔으면 좋겠다.
    • 그런데 저걸 안적어주면 make_pair를 저렇게 요청하면 내가 원하는
    • BSTPair<Str, int> pair; <Str, int> 입력하면 우리가 만든 문자열 클래스 Str로 인식한다.
    • BSTPair<wchar_t*, int> pair <Str, int>를 입력하지않으면 Str을 걍 문자배열로 인식해버린다.
  • 그렇다면 map은 저게 구현이 되어 있다는 것이다.
    • 우리가 만든 bst에도 적용할려면 insert쪽을 손봐야한다.

해결하자.

	template<typename PAIR>
	void insert(const PAIR& _pair);
  • 일단 뭐가됐던 받아.
  • T1이였던걸 애매하게 PAIR로 받겠다.
///template<typename T1, typename T2>
template<typename PAIR>
//void BST<T1, T2>::insert(const BSTPair<T1, T2>& _pair)
void BST<T1, T2>::insert(const PAIR& _pair)
{
										// BST<wchar_t[5], int> == PAIR;
	// 1                          
	//BSTNode<T1, T2>* pNewNode = new BSTNode<T1, T2>(_pair);
    // 2
	BSTNode<T1, T2>* pNewNode = new BSTNode<T1, T2>(_pair.first, _pair.second);

	// 아래 구현 부분 생략.
}
  • 노드를 새로만들떄도 이제 1번처럼이 _pair를 받는게 아니라 2번처럼 _pair.first, _pair.second 세분화해서 받는다.
    • 일단 받아서 받은 다음에 세분화해서 분석한다.
	BSTNode(const T1* _first, cosnt T2& _second, BSTNode* _Parent = nullptr, BSTNode* _LeftChild = nullptr, BSTNode* _RightChild = nullptr)
		: pair{ _first._second }
		, pParent(_Parent)
		, pLeftChild(_LeftChild)
		, pRightChild(_RightChild)
	{}
  • _pair를 세분화해서 받을 생성자도 하나 만들어야된다.
    • 퍼스트 세컨드를 개별적으로 받아오는 버전에 생성자
  • BST<wchar_t[5], int> 원하지 않은 형태로 들어오겟지만 우리가 저걸 받아서 원하는 형태로 초기화 해준다.
// BST<wchar_t[5], int> == PAIR;
  • T1을 받은게 아니라 어떤게 들어오던(BST<wchar_t[5], int>)간에 그런것들을 받는 PAIR로 받는 insert가 만들어진것이다.
  • 오류가 얼추 해결됨

추가 문제 상황

	BSTPair<wchar_t[5], int> pair;
	Str s = pair.first;

	wchar_t arr[5] = {};
	wchar_t arr2[5] = {};

	arr2 = arr;
  • 배열 5개짜리로 봐서 T1타입이 페어도 T1타입도 배열이니까 반환도 배열타입을 반환하니까 insert처럼 받을려했지만 우리 pair함수가 대입이 불가능한 상황.
  • 표준라이브러리 map은 어렵게 해결했당.
  • 나중에 해결하자.

문자열을 키로 사용한다는것에 핵심적으로 체크할 상황

  • 문자열들끼리 비교를 할려면 단순히 문자열의 주소로 키를 지정하는게 아니라 크거나 작거나 정확히 판단할 문자열 클래스가 필요하다.
  • 문자열클래스에는 문자열 비교 기준을 다 마련해놨기 때문에 그 기준을 키값으로 잡아서 진짜 문자열 을 기반으로 비교를한다.

<string.h>

	string;
  • 1바이트 문자열을 관리해주는 string 클래스
	wstring
  • 2바이트 문자열을 관리해주는 wstring 클래스
  • string이랑 string.h는 좀 다르당.

강의 코드

main.cpp

#include <iostream>
#include "BST.h"
#include "Str.h"
#include <map>
using std::map;
using std::make_pair;
#include <string.h>

int main()
{
	// 문자열 키값 사용
	// 문자열의 타입은 const char* 타입
	const wchar_t* pChar = L"asdasd";

	map<const wchar_t*, int> mapData;
	mapData.insert(make_pair(L"Father", 0));
	mapData.insert(make_pair(L"Mother", 1));
	mapData.insert(make_pair(L"Brother", 2));
	mapData.insert(make_pair(L"Sister", 3));

	map<const wchar_t*, int>::iterator iter = mapData.find(L"Father");
	iter->first;
	iter->second;
	wchar_t szName[20] = L"Father";
	mapData.find(szName);

	map<Str, int> mapString;
	mapString.insert(make_pair(L"Father", 0));
	mapString.insert(make_pair(L"Mother", 1));
	mapString.insert(make_pair(L"Brother", 2));
	mapString.insert(make_pair(L"Sister", 3));

	Str str1(L"Father");
	Str str2(L"Father");

	bool b = str1 < str2;

	// 이진탐색트리의 키값을 Str 로 지정
	BST<Str, int> mybst;

	Str mystr(L"Test");

	//BSTPair<Str, int> pair;
	//mybst.insert();
	//make_bstpair(L"Test", 0);

	BSTPair<wchar_t[5], int> pair;
	Str s = pair.first;
	// map
	std::string;
	std::wstring;

	return 0;
}

Str.h

#pragma once

class Str
{
private:
	wchar_t* m_pStr;			// 동적할당한 문자열의 주소
	int			m_MaxLength;	// 문자열을 저장할 수 있는 최대 길이
	int			m_Length;		// 문자열의 길이

public:
	int Length() { return m_Length; }
	const wchar_t* GetStr() { return m_pStr; }

private:
	void Realloc();

	// 연산자 오버로딩
public:
	void operator = (const wchar_t* _str);

	template<int Size>
	void operator =(const wchar_t(&_buffer)[Size])
	{
		(*this) = _buffer;
	}

	Str operator+ (const Str& _string);
	Str operator+ (const char* _Str);
	void operator +=(const wchar_t* _Str);

	bool operator ==(const Str& _Other) const;
	bool operator !=(const Str& _Other) const;
	bool operator <(const Str& _Other) const;
	bool operator >(const Str& _Other) const;
	bool operator <=(const Str& _Other) const;
	bool operator >=(const Str& _Other) const;


public:
	Str();
	Str(const wchar_t* _str);

	template<int Size>
	Str(const wchar_t(&_buffer)[Size])
		: m_pStr(nullptr)
		, m_MaxLength(10)
		, m_Length(0)
	{
		m_pStr = new wchar_t[m_MaxLength + 1];
		(*this) = _buffer;
	}

	~Str();
};

Str.cpp

#include "Str.h"

#include <iostream>
#include <assert.h>

Str::Str()
	: m_pStr(nullptr)
	, m_MaxLength(10)
	, m_Length(0)
{
	m_pStr = new wchar_t[m_MaxLength + 1];
}

Str::Str(const wchar_t* _str)
	: m_pStr(nullptr)
	, m_MaxLength(10)
	, m_Length(0)
{
	m_pStr = new wchar_t[m_MaxLength + 1];
	(*this) = _str;
}

Str::~Str()
{
	if (nullptr != m_pStr)
		delete m_pStr;
}



void Str::Realloc()
{
	// 수용 공간을 2배로 확장하기
	m_MaxLength *= 2;

	// 새로운 공간을 만들어낸다.
	wchar_t* pNew = new wchar_t[m_MaxLength + 1];

	// 원래 있던 데이터를 새로운곳으로 옮긴다.
	for (int i = 0; i < m_Length; ++i)
	{
		pNew[i] = m_pStr[i];
	}
	pNew[m_Length] = '\0';

	// 기존 공간을 해제한다.
	delete m_pStr;

	// 새로운 공간을 가리킨다.
	m_pStr = pNew;
}

void Str::operator=(const wchar_t* _str)
{
	// 입력되려는 문자열의 문자 개수(길이) 파악
	int len = 0;
	while ('\0' != _str[len]) { ++len; }

	// 입력되려는 문자열의 길이가 최대 수용개수를 넘어서면 저장 공간 확장
	while (m_MaxLength < len)
	{
		Realloc();
	}

	// 입력 문자열의 값을, 힙 공간으로 하나씩 옮기기
	int i = 0;
	for (; i < len; ++i)
	{
		m_pStr[i] = _str[i];
	}

	// 마지막에 널문자로 막기
	m_pStr[i] = '\0';

	// 문자열 길이 갱신(입력된 문자열 길이로)
	m_Length = len;
}

Str Str::operator+(const Str& _string)
{
	Str strNew;
	strNew = m_pStr;
	strNew += _string.m_pStr;

	return strNew;
}

Str Str::operator+(const char* _Str)
{
	Str strNew;
	return strNew;
}

void Str::operator+=(const wchar_t* _Str)
{
	// 뒤에 붙을 문자열의 문자 개수(길이) 파악
	int len = 0;
	while ('\0' != _Str[len]) { ++len; }

	// 원래 문자열 길이 + 새로 뒤에 붙을 문자열의 길이가 최대 저장 크기를 벗어나는지 확인
	while (m_MaxLength < m_Length + len)
	{
		Realloc();
	}

	// 뒤에붙을 문자열을 가져오기
	for (int i = 0; i < len; ++i)
	{
		m_pStr[i + m_Length] = _Str[i];
	}

	// 저장하고 있는 문자열 길이 갱신
	m_Length += len;
	m_pStr[m_Length] = '\0';
}

bool Str::operator==(const Str& _Other) const
{
	// 각 객체의 문자열의 길이가 동일하지 않으면 같지 않음
	if (m_Length != _Other.m_Length)
		return false;

	// 모든 문자가 같은지 비교
	for (int i = 0; i < m_Length; ++i)
	{
		// 중간에 하나라도 다르면 두 문자열은 같지 않음
		if (m_pStr[i] != _Other.m_pStr[i])
		{
			return false;
		}
	}

	// 문자열의 길이가 같고, 모든 문자가 동일하면 두 문자열은 같은 문자열이다.
	return true;
}

bool Str::operator!=(const Str& _Other)const
{
	return !((*this) == _Other);
}

bool Str::operator<(const Str& _Other)const
{
	// 비교하려는 두 문자열이 모두 문자가 하나도 없다면 비교할 수 없다.
	assert(m_Length || _Other.m_Length);

	int MinLen = m_Length < _Other.m_Length ? m_Length : _Other.m_Length;

	for (int i = 0; i < MinLen; ++i)
	{
		if (m_pStr[i] < _Other.m_pStr[i])
			return true;
		else if (m_pStr[i] > _Other.m_pStr[i])
			return false;
	}

	// 모든 문자가 동일했다면 문자열이 길이가 더 짧은 문자열이 더 작다.
	if (m_Length < _Other.m_Length)
		return true;
	else
		return false;
}

bool Str::operator >(const Str& _Other)const
{
	// 비교하려는 두 문자열이 모두 문자가 하나도 없다면 비교할 수 없다.
	assert(m_Length || _Other.m_Length);

	int MinLen = m_Length < _Other.m_Length ? m_Length : _Other.m_Length;

	for (int i = 0; i < MinLen; ++i)
	{
		if (m_pStr[i] > _Other.m_pStr[i])
			return true;
		else if (m_pStr[i] < _Other.m_pStr[i])
			return false;
	}

	// 모든 문자가 동일했다면 문자열이 길이가 더 짧은 문자열이 더 작다.
	if (m_Length > _Other.m_Length)
		return true;
	else
		return false;
}

bool Str::operator<=(const Str& _Other)const
{
	// 비교하려는 두 문자열이 모두 문자가 하나도 없다면 비교할 수 없다.
	if (!(m_Length || _Other.m_Length))
		return true;

	int MinLen = m_Length < _Other.m_Length ? m_Length : _Other.m_Length;

	for (int i = 0; i < MinLen; ++i)
	{
		if (m_pStr[i] < _Other.m_pStr[i])
			return true;
		else if (m_pStr[i] > _Other.m_pStr[i])
			return false;
	}

	// 모든 문자가 동일했다면 문자열이 길이가 더 짧은 문자열이 더 작다.
	if (m_Length <= _Other.m_Length)
		return true;
	else
		return false;
}

bool Str::operator>=(const Str& _Other)const
{
	// 비교하려는 두 문자열이 모두 문자가 하나도 없다면 비교할 수 없다.
	if (!(m_Length || _Other.m_Length))
		return true;

	int MinLen = m_Length < _Other.m_Length ? m_Length : _Other.m_Length;

	for (int i = 0; i < MinLen; ++i)
	{
		if (m_pStr[i] > _Other.m_pStr[i])
			return true;
		else if (m_pStr[i] < _Other.m_pStr[i])
			return false;
	}

	// 모든 문자가 동일했다면 문자열이 길이가 더 짧은 문자열이 더 작다.
	if (m_Length >= _Other.m_Length)
		return true;
	else
		return false;
}

1차 24.01.11
2차 24.01.12
3차 24.01.15
4차 24.01.16
5차 24.01.17

0개의 댓글