IT공부/IT서적

[윤성우 열혈 C++프로그래밍] Part4. 객체지향의 완성 - 1

shine94 2024. 9. 21. 08:36

** 연산자 오버로딩

 : 기존에 존재하던 연산자의 기본 기능 외에 다른 기능을 추가할 수 있음

   ㄴ 연산자를 오버로딩 한 함수도 const 선언 가능

 

** 연산자를 오버로딩 하는 두가지 방법

1) 멤버함수에 의한 연산자 오버로딩

#include <iostream>
using namespace std;

class Point
{
private:
	int xPos, yPos;
public:
	Point(int x = 0, int y = 0) : xPos(x), yPos(y)
	{ }
	
	void ShowPosition() const
	{
		cout << '[' << xPos << ", " << yPos << ']' << endl;
	}

	Point operator+(const Point& ref)		// 멤버함수에 의한 연산자 오버로딩
	{
		Point pos(xPos + ref.xPos, yPos + ref.yPos);
		return pos;
	}
};

int main(void)
{
	Point pos1(3, 4);
	Point pos2(10, 20);

	Point pos3 = pos1 + pos2;
	Point pos4 = pos1.operator+(pos2);

	pos1.ShowPosition();
	pos2.ShowPosition();
	pos3.ShowPosition();
	pos4.ShowPosition();
	return 0;
}

 

2) 전역함수에 의한 연산자 오버로딩

#include <iostream>
using namespace std;

class Point
{
private:
	int xPos, yPos;
public:
	Point(int x = 0, int y = 0) : xPos(x), yPos(y)
	{ }
	
	void ShowPosition() const
	{
		cout << '[' << xPos << ", " << yPos << ']' << endl;
	}

	friend Point operator+(const Point& pos1, const Point& pos2);	// 전역함수에 의한 연산자 오버로딩
};

Point operator+(const Point& pos1, const Point& pos2)			// 전역함수에 의한 연산자 오버로딩
{
	Point pos(pos1.xPos + pos2.xPos, pos1.yPos + pos2.yPos);
	return pos;
}

int main(void)
{
	Point pos1(3, 4);
	Point pos2(10, 20);

	Point pos3 = pos1 + pos2;
	Point pos4 = operator+(pos1, pos2);

	pos1.ShowPosition();
	pos2.ShowPosition();
	pos3.ShowPosition();
	pos4.ShowPosition();
	return 0;
}

 

** 객체지향에는 '전역(global)'에 대한 개념이 존재 하지 않음

   C++은 C 스타일의 코드구현이 가능한 언어이기 때문에 전역에 대한 개념이 존재

   따라서, 특별한 경우가 아니라면 멤버함수를 기반으로 연산자를 오버로딩하는 것이 좋음

 

** 멤버함수 기반 기준 - 오버로딩이 불가능한 연산자

. 멤버 접근 연산자
.* 멤버 포인터 연산자
:: 범위 지정 연산자
조건?참:거짓 조건 연산자(3항 연산자)
sizeof 바이트 단위 크기 계산
typeid RTTI 관련 연산자
static_cast 형변환 연산자
dynamic_cast 형변환 연산자
const_cast 형변환 연산자
reinterpret_cast 형변환 연산자

 

** 멤버함수 기반 기준 - 오버로딩이 가능한 연산자

= 대입연산자
() 함수 호출 연산자
[] 배열 접근 연산자
-> 멤버 접근을 위한 포인터 연산자

 

** 연산자를 오버로딩 주의사항

1) 본래의 의도를 벗어난 형태의 연산자 오버로딩은 좋지 않다

2) 연산자의 우선순위와 결합성은 바뀌지 않는다

3) 매개변수의 디폴터 값 설정이 불가능하다

4) 연산자의 순수 기능까지 빼앗을 수는 없다

 

** 단항 연산자 - 증가 연산자(++)와 감소 연산자(--)의 오버로딩

1) 전위

   ㄴ ++pos → pos.operator++();

   ㄴ --pos → pos.operator--();

#include <iostream>
using namespace std;

class Point
{
private:
	int xPos, yPos;
public:
	Point(int x = 0, int y = 0) : xPos(x), yPos(y)
	{ }
	
	void ShowPosition() const
	{
		cout << '[' << xPos << ", " << yPos << ']' << endl;
	}

	Point& operator++()
	{
		xPos += 1;
		yPos += 1;
		return *this;
	}

	friend Point& operator--(Point& ref);
};

Point& operator--(Point& ref)
{
	ref.xPos -= 1;
	ref.yPos -= 1;
	return ref;
}

int main(void)
{
	cout << "start" << endl;
	Point pos(1, 1);
	pos.ShowPosition();

	cout << "===============" << endl;

	++pos;
	pos.ShowPosition();

	--pos;
	pos.ShowPosition();

	cout << "===============" << endl;

	pos.operator++();
	pos.ShowPosition();

	operator--(pos);
	pos.ShowPosition();

	cout << "===============" << endl;

	++(++pos);
	pos.ShowPosition();

	--(--pos);
	pos.ShowPosition();
	return 0;
}

 

2) 후위

   ㄴ pos++ → pos.operator++(int);

   ㄴ pos-- → pos.operator--(int)

#include <iostream>
using namespace std;

class Point
{
private:
	int xPos, yPos;
public:
	Point(int x = 0, int y = 0) : xPos(x), yPos(y)
	{ }
	
	void ShowPosition() const
	{
		cout << '[' << xPos << ", " << yPos << ']' << endl;
	}

	const Point operator++(int)
	{
		const Point result(xPos, yPos);
		xPos += 1;
		yPos += 1;
		return result;
	}

	friend const Point operator--(Point& ref, int);
};

const Point operator--(Point& ref, int)
{
	const Point result(ref);
	ref.xPos -= 1;
	ref.yPos -= 1;
	return result;
}

int main(void)
{
	cout << "start" << endl;
	Point pos(1, 1);
	pos.ShowPosition();

	cout << "===============" << endl;

	Point cpy;

	cpy = pos++;
	
	cpy.ShowPosition();
	pos.ShowPosition();

	cout << "===============" << endl;

	cpy = pos--;
	cpy.ShowPosition();
	pos.ShowPosition();
	return 0;
}

 

https://spenshi.tistory.com/entry/%EC%A6%9D%EA%B0%90-%EC%97%B0%EC%82%B0%EC%9E%90-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9

 

[C++] 증감 연산자 오버로딩(전위 연산자, 후위 연산자)

증감 연산자 증감 연산자는 피연산자로부터 값을 더하거나 빼기 위한 단항 연산자인데, 연산이 이루어지는 시점에 따라 전위 연산자, 후위 연산자로 나뉜다. 단순히 증감 연산자의 사용만이 목

spenshi.tistory.com

 

https://velog.io/@soongle/c-%EC%A0%84%EC%9C%84-%EC%97%B0%EC%82%B0%EC%9E%90-%ED%9B%84%EC%9C%84-%EC%97%B0%EC%82%B0%EC%9E%90-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9

 

** 자료형이 다른 두 피연산자를 대상으로 하는 연산

// pos * 3
Point operator*(int num) 
{
	Point pos = Point(xPos * num, yPos * num);
	return pos;
}

// 3 * pos
Point operator*(int num, Point& ref) 
{
	Point pos = Point(ref.xPos * num, ref.yPos * num);
	return pos;
}

 

** 대입 연산자의 오버로딩

- 정의하지 않으면 디폴트 복사 생성자가 삽입

- 디폴트 복사 생성자는 멤버 대 멤버의 복사(얕은 복사)함

- 생성자 내에서 동적 할당을 한다면, 그리고 깊은 복사가 필요하다면 직접 정의 필요

 

#include <iostream>
using namespace std;

class First
{
public:
	int num1, num2;
	char ch1;
	char* word1;
	
	First(int n1 = 0, int n2 = 0, char c1 = 'A', const char* s = "Default") : num1(n1), num2(n2), ch1(c1)
	{
		word1 = new char[strlen(s) + 1];
		strcpy_s(word1, strlen(s) + 1, s);
	}

	void ShowData()
	{
		cout << num1 << ", " << num2 << ", " << ch1 << ", " << word1 << endl;
	}
};

class Second
{
public:
	int num3, num4;
	char ch3;
	char* word3;

	Second(int n3 = 0, int n4 = 0, char c3 = 'A', const char* s = "Default") : num3(n3), num4(n4), ch3(c3)
	{
		word3 = new char[strlen(s) + 1];
		strcpy_s(word3, strlen(s) + 1, s);
	}
	
	void ShowData()
	{
		cout << num3 << ", " << num4 << ", " << ch3 << ", " << word3 << endl;
	}

	Second& operator=(const Second& ref)
	{
		cout << "Second& operator=()" << endl;
		num3 = ref.num3;
		num4 = ref.num4;
		ch3 = ref.ch3;

		word3 = new char[strlen(ref.word3) + 1];
		strcpy_s(word3, strlen(ref.word3) + 1, ref.word3);

		return *this;
	}
};

int main(void)
{
	First fsrc(111, 222, 'A', "nice to meet you - 1");
	First fcpy = fsrc;

	Second ssrc(333, 444, 'B', "nice to meet you - 1");
	Second scpy = ssrc;

	fsrc.ShowData();
	fcpy.ShowData();

	cout << endl;

	ssrc.ShowData();
	scpy.ShowData();

	cout << endl << "============================" << endl << endl;

	////////////////////////////////////////////////////////////

	const char* changeWord = "nice to meet you - 2";
	
	strcpy_s(fsrc.word1, strlen(changeWord) + 1, changeWord);
	fsrc.ch1 = 'Z';

	strcpy_s(ssrc.word3, strlen(changeWord) + 1, changeWord);
	ssrc.ch3 = 'Y';

	////////////////////////////////////////////////////////////

	fsrc.ShowData();
	fcpy.ShowData();

	cout << endl;

	ssrc.ShowData();
	scpy.ShowData();

	cout << endl << "============================" << endl << endl;

	////////////////////////////////////////////////////////////

	First fob1, fob2;
	Second sob1, sob2;

	fob1 = fob2 = fsrc;
	sob1 = sob2 = ssrc;

	cout << endl << "============================" << endl << endl;

	fob1.ShowData();
	fob2.ShowData();
	
	cout << endl;

	sob1.ShowData();
	sob2.ShowData();

	cout << endl << "============================" << endl << endl;

	////////////////////////////////////////////////////////////

	const char* changeWord2 = "nice to meet you - 3";

	strcpy_s(fob1.word1, strlen(changeWord2) + 1, changeWord2);
	strcpy_s(sob1.word3, strlen(changeWord2) + 1, changeWord2);

	fsrc.ShowData();
	fcpy.ShowData();
	fob1.ShowData();
	fob2.ShowData();

	cout << endl;

	ssrc.ShowData();
	scpy.ShowData();
	sob1.ShowData();
	sob2.ShowData();
	return 0;
}

 

** 얕은 복사로 인해서, 객체의 소멸과정에서 지워진 문자열을 중복 소멸하는 문제가 발생할 수 있음

   따라서 깊은 복사로 정의하고

   메모리 누수가 발생하지 않도록, 깊은 복사에 앞서 메모리 해제의 과정을 거친다

 

** 상속 구조에서의 대입 연산자 호출

 : 유도 클래스의 대입 연산자 정의에서 명시적으로 기초 클래스의 대입 연산자 호출문을 삽입하지 않으면

   기초 클래스의 대입연산자는 호출되지 않아서,

   기초 클래스의 멤버변수는 멤버 대 멤버의 복사 대상에서 제외

 

 

#include <iostream>
using namespace std;

class First
{
private:
	int num1, num2;
public:
	First(int n1 = 0, int n2 = 0) : num1(n1), num2(n2)
	{}

	void ShowData()
	{
		cout << num1 << ", " << num2 << endl;
	}

	First& operator=(const First& ref)
	{
		cout << "Second& operator=()" << endl;
		num1 = ref.num1;
		num2 = ref.num2;
		return *this;
	}
};

class Second : public First
{
private:
	int num3, num4;
public:
	Second(int n1 = 0, int n2 = 0, int n3 = 0, int n4 = 0) 
		: First(n1, n2), num3(n3), num4(n4)
	{}

	void ShowData()
	{
		First::ShowData();
		cout << num3 << ", " << num4 << endl;
	}

	Second& operator=(const Second& ref)
	{
		cout << "Second& operator=()" << endl;
		First::operator=(ref);		// 기초 클래스의 대입 연산자 호출
		num3 = ref.num3;
		num4 = ref.num4;
		return *this;
	}
};

int main(void)
{
	Second ssrc(111, 222, 333, 444);
	Second scpy(0, 0, 0, 0);
	scpy = ssrc;
	scpy.ShowData();
	return 0;
}

 

** 이니셜라이저가 성능 향상에 도움주는 이유?

 : 이니셜라이저를 이용하면 선언과 동시에 초기화가 이뤄지는 형태로 바이너리 코드가 생성

   즉, 함수의 호출 횟수를 줄일 수 있고 초기화 과정을 단순화 시킬수 있어서 약간의 성능 향상을 기대할 수 있음

 

** 배열요소에 접근할 때 사용하는 [] 연산자

   ㄴ [문제점] C와 C++의 기본 배열은 경계 검사를 하지 않음

   ㄴ [해결책] [] 연산자 오버로딩을 통해 잘못된 배열 접근을 막을 수  있음

 

#include <iostream>
#include <cstdlib>
using namespace std;

class BoundCheckIntArray
{
private:
	int* arr;
	int arrlen;
public:
	BoundCheckIntArray(int len) : arrlen(len)
	{
		arr = new int[len];
	}
    
	int& operator[] (int idx)
	{
		if (idx < 0 || idx >= arrlen)
		{
			cout << "Array index out of bound exception" << endl;
			exit(1);
		}
		return arr[idx];
	}
    
	~BoundCheckIntArray()
	{
		delete[] arr;
	}
};

int main(void)
{
	BoundCheckIntArray arr(5);

	for (int i = 0; i < 5; i++)
		arr[i] = (i + 1) * 11;

	for (int i = 0; i < 6; i++)
		cout << arr[i] << endl;

	return 0;
}

 

#include <iostream>
#include <cstdlib>
using namespace std;

class BoundCheckIntArray
{
private:
	int* arr;
	int arrlen;
	BoundCheckIntArray(const BoundCheckIntArray& arr) {}
	BoundCheckIntArray& operator=(const BoundCheckIntArray& arr) {}
public:
	BoundCheckIntArray(int len) : arrlen(len)
	{
		arr = new int[len];
	}

	int& operator[] (int idx)
	{
		if (idx < 0 || idx >= arrlen)
		{
			cout << "Array index out of bound exception" << endl;
			exit(1);
		}
		return arr[idx];
	}

	// ShowAllData에서의 ref[len] 인덱스 연산은 컴파일 에러
	// 왜? 매개변수가 const BoundCheckIntArray& ref 이기 때문에
	// 따라서 인덱스 오버로딩 const 추가
	int& operator[] (int idx) const
	{
		if (idx < 0 || idx >= arrlen)
		{
			cout << "Array index out of bound exception" << endl;
			exit(1);
		}
		return arr[idx];
	}

	int GetArrLen() const
	{
		return arrlen;
	}

	~BoundCheckIntArray()
	{
		delete[] arr;
	}
};

void ShowAllData(const BoundCheckIntArray& ref)
{
	int len = ref.GetArrLen();
	for (int idx = 0; idx < len; idx++)
	{
		cout << ref[len] << endl;
	}
}

int main(void)
{
	BoundCheckIntArray arr(5);

	for (int i = 0; i < 5; i++)
		arr[i] = (i + 1) * 11;

	for (int i = 0; i < 6; i++)
		cout << arr[i] << endl;

	return 0;
}

 

** 객체 저장을 위한 배열 클래스 정의

 : 아래의 예제와 같이 주소값을 저장하는 경우,

   객체의 생성과 소멸을 위한 new, delete 연산 때문에 신경 쓸 것이 많아 보이지만

   깊은 복사인지 얕은 복사인지 하는 문제를 신경쓰지 않아도 되기 때문에 이 방법을 많이 사용한다고 함

#include <iostream>
#include <cstdlib>
using namespace std;

class Point
{
private:
	int xpos, ypos;
public:
	Point(int x = 0, int y = 0) : xpos(x), ypos(y)
	{}
	friend ostream& operator<<(ostream& os, const Point pos);
};

ostream& operator<<(ostream& os, const Point pos)
{
	os << '[' << pos.xpos << "," << pos.ypos << ']' << endl;
	return os;
}

typedef Point* POINT_PTR;

class BoundCheckPointPtrArray
{
private:
	POINT_PTR* arr;
	int arrlen;
	BoundCheckPointPtrArray(const BoundCheckPointPtrArray& arr) {}
	BoundCheckPointPtrArray& operator=(const BoundCheckPointPtrArray& arr) {}

public:
	BoundCheckPointPtrArray(int len) : arrlen(len) { arr = new POINT_PTR[len]; }

	POINT_PTR& operator[] (int idx)
	{
		if (idx < 0 || idx >= arrlen)
		{
			cout << "Array index out of bound exception" << endl;
			exit(1);
		}
		return arr[idx];
	}

	POINT_PTR& operator[] (int idx) const
	{
		if (idx < 0 || idx >= arrlen)
		{
			cout << "Array index out of bound exception" << endl;
			exit(1);
		}
		return arr[idx];
	}

	int GetArrLen() const { return arrlen; }

	~BoundCheckPointPtrArray() { delete[] arr; }
};

int main(void)
{
	BoundCheckPointPtrArray arr(3);
	arr[0] = new Point(3, 4);
	arr[1] = new Point(5, 6);
	arr[2] = new Point(7, 8);

	for (int i = 0; i < arr.GetArrLen(); i++)
	{
		cout << *(arr[i]);
	}

	delete arr[0];
	delete arr[1];
	delete arr[2];

	return 0;
}

 

** new와 delete도 연산자이기 때문에, 오버로딩 가능

   => new 연산자의 역할

      ① 메모리 공간의 할당

      ② 생성자의 호출

      ③ 할당하고자 하는 자료형에 맞게 반환된 주소 값을 형 변환

 

   => 1번에 해당하는 메모리 공간의 할당만 오버로딩 가능

 

   => 반환형은 반드시 void 포인터 형이어야 하고,

        매개변수형은 size_t이어야 함

        이렇게 오버로딩 된 함수는 컴파일러에 의해서 호출

 

   => size_t

        typedef unsigned int size_t;

        → 0 이상의 값을 표현할 목적으로 정의된 자료형

 

https://nomad-programmer.tistory.com/362

 

[Programming/C++] new/delete 연산자 오버로딩

new와 delete도 연산자이기 때문에 오버로딩이 가능하다. new 연산자 기본적으로 제공되는 new 연산자가 하는 일은 다음과 같다. 메모리 공간의 할당 생성자의 호출 할당하고자 하는 자료형에 맞게

nomad-programmer.tistory.com

 

#include <iostream>
#include <cstdlib>
using namespace std;

class Point
{
private:
	int xpos, ypos;
public:
	Point(int x = 0, int y = 0) : xpos(x), ypos(y)
	{}
	friend ostream& operator<<(ostream& os, const Point pos);

	void* operator new(size_t size)
	{
		cout << "opterator new : " << size << endl;
		void* adr = new char[size];
		return adr;
	}

	void operator delete (void* adr)
	{
		cout << "opterator delete ()" << endl;
		delete[] adr;
	}
};

ostream& operator<<(ostream& os, const Point pos)
{
	os << '[' << pos.xpos << "," << pos.ypos << ']';
	return os;
}

int main(void)
{
	Point* ptr = new Point(3, 4);
	cout << *ptr << endl;
	delete ptr;

	return 0;
}

 

** 스마트  포인터(Smart Pointer)

   스마트 포인터는 객체

   즉, 포인터의 역할을 하는 객체라는 의미임

#include <iostream>
#include <cstdlib>
using namespace std;

class Point
{
private:
	int xpos, ypos;
public:
	Point(int x = 0, int y = 0) : xpos(x), ypos(y)
	{
		cout << "Point 객체 생성" << endl;
	}
	~Point()
	{
		cout << "Point 객체 소멸" << endl;
	}

	void SetPos(int x, int y) 
	{
		xpos = x;
		ypos = y;
	}

	friend ostream& operator<<(ostream& os, const Point& pos);
};

ostream& operator<<(ostream& os, const Point& pos)
{
	os << '[' << pos.xpos << "," << pos.ypos << ']' << endl;
	return os;
}

class SmartPtr
{
private:
	Point* posptr;
public:
	SmartPtr(Point* ptr) : posptr(ptr)
	{}

	// 스마트 포인터의 기본 함수 - 1
	Point& operator*() const
	{
		return *posptr;
	}

	// 스마트 포인터의 기본 함수 - 2
	Point* operator->() const
	{
		return posptr;
	}

	~SmartPtr()
	{
		delete posptr;
	}
};

int main(void)
{
	SmartPtr sptr1(new Point(1, 2));
	SmartPtr sptr2(new Point(3, 4));
	SmartPtr sptr3(new Point(5, 6));

	cout << endl;

	cout << *sptr1;
	cout << *sptr2;
	cout << *sptr3;

	cout << endl;

	sptr1->SetPos(10, 20);
	sptr2->SetPos(30, 40);
	sptr3->SetPos(50, 60);

	cout << *sptr1;
	cout << *sptr2;
	cout << *sptr3;

	cout << endl;
	
	return 0;
}

 

** 펑터(Functor)

 : 객체를 함수처럼 사용 가능, 함수 또는 객체의 동작방식에 유연함을 제공할 때 주로 사용

   ㄴ () 연산자 : 함수에 호출에 사용되는, 인자의 전달에 사용, 오버로딩 가능

#include <iostream>
using namespace std;

class Point
{
private:
	int xpos, ypos;
public:
	Point(int x = 0, int y = 0) : xpos(x), ypos(y)
	{ }

	Point operator+(const Point& pos) const
	{
		return Point(xpos + pos.xpos, ypos + pos.ypos);
	}

	friend ostream& operator<<(ostream& os, const Point& pos);
};

ostream& operator<<(ostream& os, const Point& pos)
{
	os << '[' << pos.xpos << "," << pos.ypos << ']' << endl;
	return os;
}

// 펑터(Functor) == 함수 오브젝트(Function Object)
class Adder
{
public:
	int operator()(const int& n1, const int& n2) 
	{
		return n1 + n2;
	}

	double operator()(const double& e1, const double& e2)
	{
		return e1 + e2;
	}
	Point operator()(const Point& pos1, const Point& pos2)
	{
		return pos1 + pos2;
	}
};

int main(void)
{
	Adder adder;

	cout << adder(1, 3) << endl;
	cout << adder(1.5, 3.7) << endl;
	cout << adder(Point(3, 4), Point(7, 9));
	
	return 0;
}

 

#include <iostream>
using namespace std;

class SortRule
{
public:
	virtual bool operator()(int num1, int num2) const = 0;
};

class AscendingSort : public SortRule		// 오름차순
{
public:
	bool operator()(int num1, int num2) const
	{
		if (num1 > num2)
			return true;
		else
			return false;
	}
};

class DescendingSort : public SortRule		// 내림차순
{
	bool operator()(int num1, int num2) const
	{
		if (num1 < num2)
			return true;
		else
			return false;
	}
};

class DataStorage							// int형 정수의 저장소
{
private:
	int* arr;
	int idx;
	const int MAX_LEN;

public:
	DataStorage(int arrlen) : idx(0), MAX_LEN(arrlen)
	{
		arr = new int[MAX_LEN];
	}

	void AddData(int num)
	{
		if (MAX_LEN <= idx)
		{
			cout << "더 이상 저장이 불가능합니다." << endl;
			return;
		}
		arr[idx++] = num;
	}

	void ShowAllData()
	{
		for (int i = 0; i < idx; i++)
		{
			cout << arr[i] << "   ";
		}
		cout << endl;
	}

	void SrotData(const SortRule& functor)
	{
		for (int i = 0; i < idx - 1; i++)
		{
			for (int j = 0; j < idx - 1 - i; j++)
			{
				if (functor(arr[j], arr[j + 1]))
				{
					int temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
				}
			}
		}
	}
};

int main(void)
{
	DataStorage storage(5);
	storage.AddData(40);
	storage.AddData(30);
	storage.AddData(50);
	storage.AddData(20);
	storage.AddData(10);

	storage.SrotData(AscendingSort());
	storage.ShowAllData();

	storage.SrotData(DescendingSort());
	storage.ShowAllData();

	storage.AddData(100);

	return 0;
}

 

** 임시객체로의 자동 형 변환과 형 변환 연산자(Conversion Operator) => 일치하지 않는 자료형 간의 대입 연산

1) 기본 자료형 데이터를 객체로 형 변환하는 것은 적절한 생성자의 정의를 통해서 가능

   (예) num = 30;

num = Number(30);		// 1단계. 임시객체 생성
num.operator=(Number(30));	// 2단계. 임시객체를 대상으로 하는 대입 연산자 호출

 

   => A형 객체가 와야 할 위치에 B형 데이터(또는 객체)가 왔을 경우,

        B형 데이터를 인자로 받는 A형 클래스의 생성자 호출을 통해서 A형 임시객체를 생성함

 

2) 객체를 기본 자료형 데이터로 형 변환 가능

   (예) Number num2 = num1 + 20; 의 연산을 하기 위한 형 변환 연산자

operator int ()
{
	return num;
}

 

▼ 1)과 2)가 반영된 소스 코드

#include <iostream>
using namespace std;

class Number
{
private:
	int num;
public:
	Number(int n = 0) : num(n)
	{
		cout << "Number(int n = " << num << ")" << endl;
	}

	Number& operator=(const Number& ref)
	{
		cout << "operator=()" << endl;
		num = ref.num;
		return *this;
	}

	operator int()		// 형 변환 연산자의 오버로딩
	{
		return num;
	}

	void ShowNumber() { cout << num << endl; }
};

int main(void)
{
	Number num1;
	num1 = 30;		// 일치하지 않는 자료형 간의 대입연산

	Number num2 = num1 + 20;
	num2.ShowNumber();

	return 0;
}

 

 

** 해당 글은 윤성우의 열혈 C++ 프로그래밍 도서를 읽고 정리한 글입니다.

   https://product.kyobobook.co.kr/detail/S000001589147

 

윤성우의 열혈 C++ 프로그래밍 | 윤성우 - 교보문고

윤성우의 열혈 C++ 프로그래밍 | 『C++ 프로그래밍』은 C언어를 이해하고 있다는 가정하에서 집필된 C++ 기본서로서, 초보자에게 적절한 설명과 예제를 통해서 C++ 프로그래밍에 대해 설명한다. 더

product.kyobobook.co.kr