클래스 (3)

Yama·2023년 12월 22일
0

어소트락 수업

목록 보기
23/55

어제 까지 만들어둔거.

#include <iostream>

// 연산자 오버로딩
class String
{
private:
	char* m_pStr;			
	int		m_MaxLength;
	int		m_Length;		

public:
	int Length()
	{
		return m_Length;
	}

	const char* GetStr()
	{
		return m_pStr;
	}

public:
	void operator = (const char* _str)
	{

	}

	String operator+ (String _string)
	{
		return String();
	}

	void operator +=(const char* _Str)
	{

	}

public:
	String()
		: m_pStr(nullptr)
		, m_MaxLength(10)
		, m_Length(0)
	{
		m_pStr = (char*)malloc(sizeof(char) * (m_MaxLength + 1));
	}

	~String()
	{
		if (nullptr != m_pStr)
			free(m_pStr);
	}
};



int main()
{
	String str;
	str = "abc";


	return 0;
}

문자열 대입 연산자 구현

	void operator = (const char* _str)
	{
		int len = 0;
		while ('\0' != _str[len]) { ++len; } // 1

		if (m_MaxLength < len) // 2
		{
			Realloc();
		}

		int i = 0;
		for (; i < len; ++i) // 3
		{
			m_pStr[i] = _str[i];
		}
        
		m_pStr[i] = '\0'; // 4

		m_Length = len; // 5

  • 문자열의 끝에는 널 문자가 있다는걸 토대로 구현 할것이다.
  • // 1은 입력되려는 문자열의 문자 개수(길이) 파악
    • while문 돌려서 널 될떄 반복문을 끝낸다.
  • // 2은 입력되려는 문자열의 길이가 최대 수용 개수를 넘어서면 저장 공간 확장
    • m_MaxLenght <= len 아닌 이유 "abc"를 입력받으면 맥스렝스가 3 < 3 이니까 문제 없다. "abcd"문자 확장해줘야 하니까 m_MaxLenght < len가 맞다.
  • // 3 입력 문자열의 값을, 힙 공간으로 하나씩 옮기기
  • // 4 마지막에 널문자로 막기
  • // 5 문자열 길이 갱신(입력된 문자열 길이로)

String을 생성과 동시에 대입 하는법.

// 기존까지 만든 클래스 생략.

int main()
{
	String str;
    str = "abcdef" 				// 1
    str.operator=("abcdef");	// 2
    
	String str 2 = "hello"; 	// 3
    
    return 0;
}
  • // 2는 1의 생략(축약)버전이다.
  • 특수하게 연산자를 맴버함수여서 이런 정확한 정석 방식으로 안해도 1번을 2번 처럼 호출해준다.
  • 3번처럼 개체를 생성과 동시에 대입을 하면 // 1번 케이스처럼 보지않는다.
class CTest
{
private:
	int m_i;
public:
	void operator = (int i)  // 3
	{
		this->m_i = i;
	}
public:
	CTest() {}
	CTest(int _i) // 5
		: m_i(_i)
	{}
	~CTest() {}
};

int main()
{	
	CTest t; // 1
  	t = 10; // 2
 
    CTest t = 10; // 4
}
  • 1번에 t라는 함수의 맴버함수를 호출한 애가 this에 들어가서 개체인 m_i에 10이 대입된다.
  • 4번을 대입 연산자 처럼 취급을 한다면 어차피 클래스 하나 생성할떄는 생성자 하나 호출한다.
    • 4번 입장에서는 작업이 2개다.
      1. 기본 생성자 호출 t라는 개체만들고
      2. t한테 대입연산자호출해서 10을 입력받는것.
  • 근데 4번은 컴파일러 입장에서 생각해보면 생성자중에 인트하나 받는 5번처럼 생성자가 있어서 그걸로 맴버를 이니셜라이징(초기화)하면된다.
    • 개체를 선언과 동시에 대입을 하는건 5번을 생성자를 하나 준비해서 한번에 처리하자.

String을 생성과 동시에 대입 받는법 (2)

	String(const char* _str)
		: m_pStr(nullptr)
		, m_MaxLength(10)
		, m_Length(0)
	{
		m_pStr = (char*)malloc(sizeof(char) * (m_MaxLength + 1));

		(*this) = _str; // 1
	}
  • String 클래스에 저걸 추가하면 대입이 가능해진다.
  • 이미 구현해둔 위에 대입 연산자를 사용해서 (*this)(나)한테 입력으로 들어온 _str를 대입 시킨다.

요약

  • 기본생성자를 호출시켜서 만든 다음에 내가 만든 대입 연산자를 사용하게 할수도 있고.
  • 생성과 동시에 대입을 했을경우 생성자를 호출시킬려하기 떄문에 대입연산자를 만들어서 사용한게 아니라 "hello"를 입력받는 버전에 생성자를 하나 더 구현해주고 그 안에서 내가 만든 대입 연산자(=)을 또 재호출해서 재사용하면된다.
	String str 2 = "hello";
  • 개체 생성과 동시에 대입이 발생하면 컴파일러는 생성자 한번으로 문제를 해결하려고 한다. 따라서 대입 연산자를 호출했을 떄랑 동일한 형태의 생성자를 준비해서 똑같은 동작을 수행할 수 있도록 한다.

CQust 클래스

class CQuest
{
private:
	int m_i;

public:
	void operator = (int i)
	{
		m_i = i;
		printf("대입 연산자\n");
	}
public:
	CQuest()
		: m_i(0)
	{
		printf("기본 생성자\n");
	}
	CQuest(int i)
		: m_i(i)
	{
		printf("생성자 호출\n");
	}
};

int main()
{
    CQuset q1;			// 1
    CQuset q2 = 10;		// 2
    
	return 0;
}
  • // 1번은 기본 생성자 호출이 나오고
  • // 2번은 대입 연산자가 아니라 생성자 호출이 출력이 된다.

문자열 덧셈 연산자 구현

  • 지역변수를 만들어서 복사하는것보다 원본을 가져와서 하는게 더 효율적일것이다.
    • 지역변수로 복사해서 하면 일일이 할일이 너무 추가되기 떄문이다.
    • 원본 레퍼런스 포인터를 이용함.
	// 대입 연산 구현 생략.

    String operator+ (const String* _string)
	{
		this;
        _string
	}


	int main()
	{
		String str1;
		str1 = "abcdef";
    
		String str2 = "gh";
    
		String str3 = str1 + &str2  // 1

		return 0;
	}
  • 포인터를 이용해서 문자열 +연산자를 구현할것이라면 1번처럼 해야되는데 뭔가 해괴망측하다.
int a, b, c;
c = a + &b;
  • 기본자료형에서 1번은 이런 느낌일것이다.
  • 내가 원하는 방식은 c = a + b; 를원하기 떄문에 레퍼런스로 구현하는게 맞을것이다.
String operator+ (const String& _string)
  • 입력으로 들어온 애를 참조하겠다. this는str1의 주소고 _string은 str2다.
    • 모양상 나는 c = a + b를 원하기 떄문에
  • 원래라면 주소가 올것이고 주소를 수정하겠다는 것은 포인터랑 같지만 C++에 추가된 레퍼런스도 원본을 수정하니까 모양상 내가 레퍼런스를 사용하면 내가 원하는 형태로 구현가능하다.
  • const붙는 이유는 문자열을 합쳐서 새로운 결과를 만드는 거니까 원본이 수정될일이 없다.
    • 3 + 4 = 7인것처럼 그래서 const를 붙여서 명시적으로 수정을 막았다.
	String operator+ (const String& _string)
	{
		String strNew;
		strNew = this->m_pStr;		// 1
		strNew += _string.m_pStr;	// 2

		return strNew;				// 3
	}
    
    int main()
	{
		String str1;
		str1 = "abcdef";
    
		String str2 = "gh";
    
		String str3 = str1 + str2  // 1

		return 0;
	}
    • 연산자 구현 부분이다.
    • 1번은 기존의 str1거의 주소를 this를 통해서 알려줬다.
    • 2번은 전달받은 str2의 값을 += 연산해서 뒤에 기존의 str1에 뒤에 붙인다.
    • 3번을 리턴하면 내가 원하는 문자열 합치기가 완성된다.

문자열 += 연산자 구현.

	void operator +=(const char* _Str)
	{
		// 3
		int len = 0;
		while ('\0' != _Str[len]) { ++len; }

		// 4
		while (m_MaxLength < m_Length + len)
		{
			Realloc();
		}

		// 5
		for (int i = 0; i < len; ++i)
		{
			m_pStr[i + m_Length] = _Str[i];
		}

		// 6
		m_Length += len;
        // 7 
		m_pStr[m_Length] = '\0';
	}
    
    int main()
	{
		String str1;
		str1 = "abc";		// 1
		str1 += "kkk";		// 2
		String str2 = "gh";
		String str3 = str1 + str2;

		printf("%s\n", str3.GetStr());
    	return 0;
	}
  • m_pStr이 개체를 가지고 있고 주소로 가보면 힙 메모리에 동적할당되서 내가 관리하는 문자열을 읽는다.
  • 2번처럼 kkk를 붙일려면
    1. 뒤에 붙을 문자열의 문자 개수(길이) 파악. // 3
    2. 원래 문자열 길이 + 새로 뒤에 붙을 문자열의 길이가 최대 저장 크기를 벗어나는지 확인 // 4
    3. 뒤에붙을 문자열을 가져오기. // 5
    4. 저장하고 있는 문자열 길이 갱신 // 6
    5. 끝에 널 문자로 막아주기. // 7

재할당 함수

private: // 1
	void Realloc()
	{
		m_MaxLength *= 2.f; // 2

		char* pNew = (char*)malloc(sizeof(char) * m_MaxLength); // 3

		for (int i = 0; i < m_Length; ++i) // 4
		{
			pNew[i] = m_pStr[i]; 
		}
		pNew[m_Length] = '\0'; // 5

		free(m_pStr); // 6

		m_pStr = pNew; // 7
	}
  • 재할당 함수는 내부에서 사용할것이기때문에 private로 하자. // 1
  • 2번은 수용 공간을 2배로 확장하기
  • 3번은 새로운 공간을 할당하기.
  • 4번은 원래 있던 데이터를 새로운곳으로 옮긴다.
  • 5번은 마지막 문자열끝을 널로막기
  • 6번은 기존의 공간 해제하기
  • 7번은 새로운 공간을 가리킨다.

강의 코드

main.cpp

#include <iostream>

class String
{
private:
	char* 	m_pStr;			
	int		m_MaxLength;	
	int		m_Length;		

public:
	int Length()
	{
		return m_Length;
	}

	const char* GetStr()
	{
		return m_pStr;
	}

private:
	void Realloc()
	{
		m_MaxLength *= 2.f;

		char* pNew = (char*)malloc(sizeof(char) * m_MaxLength);

		for (int i = 0; i < m_Length; ++i)
		{
			pNew[i] = m_pStr[i];
		}
		pNew[m_Length] = '\0';

		free(m_pStr);

		m_pStr = pNew;
	}

public:
	void operator = (const char* _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;
	}

	String operator+ (const String& _string)
	{
		String strNew;
		strNew = this->m_pStr;
		strNew += _string.m_pStr;

		return strNew;
	}

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

	void operator +=(const char* _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';
	}
    
public:
	String()
		: m_pStr(nullptr)
		, m_MaxLength(10)
		, m_Length(0)
	{
		m_pStr = (char*)malloc(sizeof(char) * (m_MaxLength + 1));
	}

	String(const char* _str)
		: m_pStr(nullptr)
		, m_MaxLength(10)
		, m_Length(0)
	{
		m_pStr = (char*)malloc(sizeof(char) * (m_MaxLength + 1));

		(*this) = _str;
	}

	~String()
	{
		if (nullptr != m_pStr)
			free(m_pStr);
	}
};

class CQuest
{
private:
	int m_i;

public:
	void operator =(int i)
	{
		m_i = i;
		printf("대입 연산자\n");
	}

public:
	CQuest()
		: m_i(0)
	{
		printf("기본 생성자\n");
	}

	CQuest(int i)
		: m_i(i)
	{
		printf("생성자 호출\n");
	}
};

int main()
{
	String str1;
	str1 = "abc";
	str1 += "kkk";

	String str2 = "gh";
	String str3 = str1 + str2;

	printf("%s\n", str3.GetStr());

	String str4 = "hello";
	CQuest q1;
	CQuest q2 = 10;

	return 0;
}

1차 23.12.22
2차 23.12.25
3차 23.12.26
4차 23.12.27
5차 23.12.28
6차 24.01.02

0개의 댓글