C++ 프로그래밍/자료구조와 알고리즘

[자료구조와 알고리즘] String Class Operator Overloading

SW Developer 2024. 2. 28. 20:09

String Class Operator Overloading

: String Class에 연산자를 오버로딩해보자<<

<<

 

1. Operator Overloading이란?

연산자를 중복정의하는 것을 의미한다

 

① 사용자가 정의한 Type에 연산자를 새로 정의할 수 있다

② Operator 오버로딩 시, 읽기 쉬운 코드가 되는 것을 지향해야 한다

③ Function Overloading과 유사하다

④ 연산자 중복정의는 아래 2가지 방법으로 가능하다

- 멤버함수로 정의

- friend를 이용한 외부함수 정의

 

 

※ 예시코드

① 복소수 Class Complex 정의 시, operator+와 operator- 를 overloading

class Complex //복소수 a + bi
{
private :
	double m_real, m_imag;
public :
	Complex() { m_real = m_imag = 0.0; }
    
    //멤버함수로 정의: Complex c 앞에 this가 숨겨져 있다
    //Complex operator+(this, Complex c);
	Complex operator+(Complex c);
	
    //friend로 정의: Complex Class 외부의 함수도 접근 가능하게 한다
        friend Complex operator-(Complex c1, Complex c2);
};

 

※ this 포인터?

① 객체자신을 가리키는 포인터이다

② 모든 멤버함수들의 숨겨진 인자를 의미한다 (단, static 멤버함수는 제외)

 

※ friend ?

클래스 외부의 함수이더라도, 클래스의 멤버에 접근 가능하도록 한다

 

② operator+와 operator- 를 overloading 하는 코드 정의 작성

Complex Complex::operator+(Complex c)//operator+가 Complex의 멤버함수라는 의미
{
	Complex c3;
	c3.m_real = m_real + c.m_real;
	c3.m_imag = m_imag + c.m_imag;
	return c3;
}

Complex operator-(Complex c1, Complex c2) //operator-가 외부함수라는 의미
{
	Complex c3;
	c3.m_real = c1.m_real - c2.m_real;//Complex 클래스의 멤버에 접근 가능
	c3.m_imag = c1.m_imag - c2.m_imag;
	return c3;
}

 

③ main 코드 작성

int main() {

	Complex c1, c2, c3;

	c3 = c1 + c2; //c3 = c1.operator+(   c2);
	c3 = c1 - c2; //c3 = operator-(c1,c2);

}

 

 

 

2. Copy Constructor vs Assignment

 

복제 생성자와 할당, 이 둘에 대한 정의 구분이 왜 필요할까? 그 이유는 바로 어떤 것을 복사한다는 copy라는 행위 자체가 단순히 값을 복사하기 위한 shallow copy와 주소 메모리까지 새롭게 할당하는 deep copy 두 가지로 이루어져 있기 때문이다.

 

- Copy Constructor (deep copy): 초기상태에서 시작한다

String str1("String1");
String str2 = str1;

 

 

 

- Assignment (shallow copy): 기존 데이터를 지운 후 새로운 값을 할당시킨다

String str1("Test String");
String str2;

str2 = str1;

 

 

 

3. 연산자의 우선순위와 결합순서

 

① 연산자 우선순위 (Precedence): 서로 다른 연산자들 중 어떤 연산자를 우선으로 연산할까?

 

②  결합순서

- 동일한 연산자가 여러 항 사용될 경우 어떤 연산자부터 결합되

- 할당 연산자는 ← 방향이며, 그 외 대부분의 연산자는 → 방향이다

//operator+, → 방향
str = String("aaa") + "bbb" + "ccc";

//operator=, ← 방향
str1 = str2 = str3 = str4;

 


③  연산자별 우선순위 및 결합순서 참고

Operator Description Precedence
:: Scope resolution Left-to-right →
a++   a-- Suffix/postfix increment and decrement
type()   type{} Functional cast
a() Function call
a[] Subscript
.   -> Member access
++a   --a Prefix increment and decrement Right-to-left ←
+a   -a Unary plus and minus
!   ~ Logical NOT and bitwise NOT
(type) C-style cast
*a Indirection (dereference)
&a Address-of
sizeof Size-of[note 1]
co_await await-expression (C++20)
new   new[] Dynamic memory allocation
delete   delete[] Dynamic memory deallocation
.*   ->* Pointer-to-member Left-to-right →
a*b   a/b   a%b Multiplication, division, and remainder
a+b   a-b Addition and subtraction
<<   >> Bitwise left shift and right shift
<=> Three-way comparison operator (since C++20)
<   <=   >   >= For relational operators < and ≤ and > and ≥ respectively
==   != For equality operators = and ≠ respectively
a&b Bitwise AND
^ Bitwise XOR (exclusive or)
| Bitwise OR (inclusive or)
&& Logical AND
|| Logical OR
a?b:c Ternary conditional[note 2] Right-to-left ←
throw throw operator
co_yield yield-expression (C++20)
= Direct assignment (provided by default for C++ classes)
+=   -= Compound assignment by sum and difference
*=   /=   %= Compound assignment by product, quotient, and remainder
<<=   >>= Compound assignment by bitwise left shift and right shift
&=   ^=   |= Compound assignment by bitwise AND, XOR, and OR
, Comma Left-to-right →

 

 

4. 두 Type 간의 상호변환

 

① 두 Type 모두 User Defined Class인 경우: 상호간의 Class Type을 인자로 하는 생성자가 필요하다

A::A(B); //Class B를 인자로 하는 A 생성자 필요
B::B(A); //Class A를 인자로 하는 B 생성자 필요

 

② 한 Type은 Class, 다른 Type은 Primitive Data Type인 경우

//String Class에 Primitive Data Type인 char형을 인자로 하는 생성자
String::String(const char*); 

//String 연산자 operator에 char형을 인자로 받기
String::operator const char*(); 

//사용예시
str2 = (const char*)str1;

 


자 이제 마지막으로, String Class에 각종 연산자들을 overloading하는 코드들을 추가해보자

 

 

① String Class 정의에 =, +=, +, << 연산자 Overloading 원형 추가

class String
{
    protected:
        int len;
        char* m_pBuffer; //string 버퍼
        void _Init();
        void _Clear();
        void _Copy(const char* lpsz);
    public:
        //constructors,destructor
        String(); //default constructor
        String(const String&); //copy constructor
        String(char*);
        String(const char*);
        String(char);
        String(long);
        String(int);
        String(double);
        ~String();

        //operators
        const String& operator=(const String&);
        const String& operator+=(const String&);
        String operator+(const String&);
        friend ostream& operator<<(ostream&, const String&);
        
        //other member functions: 추후 추가 예정

};

 

 

② String Class의 함수 정의 부분에 operator 연산자들을 overloading하기 위한 코드들을 추가

//(추가) operator 연산자 Overloading
const String& String::operator=(const String& stringSrc)
{
	_Clear();
	len = stringSrc.len;
	_Copy(stringSrc.m_pBuffer);
	return *this;
}
const String& String::operator+=(const String& stringSrc) 
{
	len = len + stringSrc.len - 1;
	char* ctemp = new char[len];
	strcpy(ctemp, m_pBuffer);
	strcat(ctemp, stringSrc.m_pBuffer);
	if (m_pBuffer != NULL)
	{
		delete[]m_pBuffer;
	}
	m_pBuffer = ctemp;

	return *this;
}
String String::operator+(const String& stringSrc)
{
	char* temp= new char[len + stringSrc.len - 1];
	strcpy(temp, m_pBuffer);
	strcat(temp, stringSrc.m_pBuffer);

	String stemp(temp);
	delete[] temp;

	return stemp;
}
ostream& operator<<(ostream& os, const String& stringSrc)
{ 
	os << stringSrc.m_pBuffer << endl;
	return os; 
}


//(기존) 생성자,파괴자
String::String() {
	_Init();
}
String::String(const String& stringSrc) {
	_Init();
	len = strlen(stringSrc.m_pBuffer);
	_Copy(stringSrc.m_pBuffer);
}
String::String(const char* lpsz) {
	_Init();
	len = strlen(lpsz) + 1;
	_Copy(lpsz);
}
String::String(char* lpsz) {
	_Init();
	len = strlen(lpsz) + 1;
	_Copy(lpsz);
}
String::String(char ch) {
	char str[2];
	str[0] = ch;
	str[1] = '\0';

	_Init();
	len = 2;
	_Copy(str);
}
String::String(long l) {
	_Init();
	
	char num_char[10 + sizeof(char)];
	sprintf(num_char, "%d", l);
	len = strlen(num_char);
	_Copy(num_char);
}
String::String(int i) {
	_Init();

	char num_char[10 + sizeof(char)];
	sprintf(num_char, "%d", i);
	len = strlen(num_char);
	_Copy(num_char);
}
String::String(double d) {
	_Init();
	
	char num_char[10 + sizeof(char)];
	sprintf(num_char, "%d", (int)d);
	len = strlen(num_char);
	_Copy(num_char);
}
String::~String() {
	_Clear();
}


//(기존) void 함수
void String::_Init() {
	len = 0;
	m_pBuffer = 0;
}
void String::_Clear() {
	if (m_pBuffer)//m_pBuffer != 0 일 때
		delete[] m_pBuffer;
	_Init();
}
void String::_Copy(const char* lpsz) {
	if (lpsz != 0) {
		m_pBuffer = new char[strlen(lpsz) + 1];//null이 저장되는 공간 +1 추가
		if (m_pBuffer)
			strcpy(m_pBuffer, lpsz);
	}
}

 

 

③ main 코드 작성

int main() {
	//Constructor
	String str1;              //default Constructor
	String str2("I can do");  //Constructor
	String str3(" it!!");     //Constructor
	String str4('A');         //Constructor
	String str5 = str4;       //Copy Constructor
	String str6(156);         //Constructor
	String str7(1.1);         //Constructor

	//cout << str1;
	cout << str2;
	cout << str3;
	cout << str4;
	cout << str5;
	cout << str6;
	cout << str7;

	String str8 = str2 + str3;
	cout << str8;

	String str9("I knew");
	str9 += str3;
	cout << str9;
}

 

 

출력 결과

 

 

 

 

 

 

※ 해당 게시글은 개인 학습의 목적으로, 아래 강의를 수강한 후 정리한 학습노트입니다.

https://inf.run/LNCU