I'm FanJae.

[C++ 기본 연습 문제] Chapter 11. 연산자 오버로딩 II 본문

C++/Basic Practice

[C++ 기본 연습 문제] Chapter 11. 연산자 오버로딩 II

FanJae 2024. 9. 12. 23:28

1. Chatper 11. 연산자 오버로딩 II

 

1-1. 깊은 복사를 하는 대입 연산자의 정의

① Chapter 07에서는 예제 HASComposite.cpp를 통해서 다음의 두 클래스를 정의하였다.

class Gun
{
        ....
}
class Police
{
private:
        int handcuffs; //소유한 수갑의 수
        Gun *pistol; //소유하고 있는 권총
public:
        ....
}

- 이에 Police 클래스를 대상으로 깊은 복사가 이뤄지도록 대입 연산자와 복사 생성자를 동시에 정의하고 이의 확인을 위해 main 함수도 적절히 정의해보자.

#include <iostream>
#include <cstring>

class Gun
{
private:
	int bullet;    	// 장전된 총알의 수
public:
	Gun(int bnum) : bullet(bnum)
	{ }
	void Shut()
	{
		std::cout << "BBANG!" << std::endl;
		bullet--;
	}
};

class Police
{
private:
	int handcuffs;    // 소유한 수갑의 수
	Gun* pistol;     // 소유하고 있는 권총
public:
	Police(int bnum, int bcuff)
		: handcuffs(bcuff)
	{
		if (bnum > 0)
			pistol = new Gun(bnum);
		else
			pistol = NULL;
	}
	void PutHandcuff()
	{
		std::cout << handcuffs << std::endl;
		std::cout << "SNAP!" << std::endl;
		handcuffs--;
	}
	void Shut()
	{
		if (pistol == NULL)
			std::cout << "Hut BBANG!" << std::endl;
		else
			pistol->Shut();
	}
	~Police()
	{
		if (pistol != NULL)
			delete pistol;
	}
	Police(const Police& ref) : handcuffs(ref.handcuffs) // 복사 생성자
	{
		std::cout << "Call the copy constructor" << std::endl;
		if (ref.pistol != NULL)
		{
			pistol = new Gun(*ref.pistol);
		}
		else
		{
			pistol = NULL;
		}
	}
	Police& operator=(const Police& ref) // 대입 생성자
	{
		std::cout << "Call the assignment operator" << std::endl;
		this->handcuffs = ref.handcuffs;
		if (pistol != NULL)
		{
			delete pistol;
		}
		if (ref.pistol != NULL)
		{
			pistol = new Gun(*ref.pistol);
		}
		else
		{
			pistol = NULL;
		}
		return *this;
	}
};

int main(void)
{
	Police pman1(10, 10);
	Police pman2 = pman1;

	Police pman3(10, 5);
	pman3 = pman2;

	pman3.PutHandcuff();
	pman3.Shut();
}

 

② Chapter 07의 문제 07-2의 두번 째 문제에서는 다음의 두 클래스 정의를 요구하였다.

 

class Book
{
private:
        char *title;    //책의 제목
        char *isbn;   //국제표준도서번호
        int price;      //책의 정가
        ....
};
 
class EBook :public Book
{
private:
        char *DRMKey; //보안관련 키
        ....
};

- 이 때 정의한 두 클래스를 대상으로 Book 클래스도, EBook 클래스도 깊은 복사가 진행이 되도록,

- 복사생성자와 대입 연산자를 정의하고이의 확인을 위한 main 함수도 적절히 정의해보자.

- 참고로 이 문제의 해결을 위해서는 여러분이 생각해봐야 할 요소들이 몇 가지 존재한다.
- 특히 앞서 말한 다음 사실을 완전히 이해한 다음에 이 문제를 해결하기 바란다.
- "C++에서, AAA형 참조자는 AAA 객체 또는 AAA를 직접 혹은 간접적으로 상속하는 모든 객체를 참조할 수 있다."

#include <iostream>

class Book
{
private:
	char* title; //책의 제목
	char* isbn; //국제표준도서번호
	int price; //책의 정가
public:
	Book(const char* _title, const char* _isbn, int _price)
		: price(_price)
	{
		size_t len = strlen(_title) + 1;
		title = new char[len];
		strcpy_s(title, len, _title);
		size_t len1 = strlen(_isbn) + 1;
		isbn = new char[len1];
		strcpy_s(isbn, len1, _isbn);
	}
	void ShowBookInfo() const
	{
		std::cout << "Title : " << title << std::endl;
		std::cout << "ISBN : " << isbn << std::endl;
		std::cout << "Price : " << price << std::endl;
	}
	~Book()
	{
		delete[]title;
		delete[]isbn;
	}
	Book(const Book& ref) : price(ref.price)
	{
		std::cout << "Call by copy constructor" << std::endl;
		size_t len = strlen(ref.title) + 1;
		title = new char[len];
		strcpy_s(title, len, ref.title);
		size_t len2 = strlen(ref.isbn) + 1;
		isbn = new char[len2];
		strcpy_s(isbn, len2, ref.isbn);
	}

	Book& operator=(const Book& ref) 
	{
		std::cout << "Call by assignment constructor" << std::endl;

		this->price = ref.price;
		if (title != NULL)
		{
			delete[] title;
		}
		if (isbn != NULL)
		{
			delete[] isbn;
		}

		size_t len = strlen(ref.title) + 1;
		title = new char[len];
		strcpy_s(title, len, ref.title);
		size_t len2 = strlen(ref.isbn) + 1;
		isbn = new char[len2];
		strcpy_s(isbn, len2, ref.isbn);

		return *this;
	}
};

class Ebook :public Book
{
private:
	char* DRMKey; //보안관련 키
public:
	Ebook(const char* a, const char* b, int c, const char* _drmkey) :Book(a, b, c)
	{
		size_t len = strlen(_drmkey) + 1;
		DRMKey = new char[len];
		strcpy_s(DRMKey, len, _drmkey);
	}
	void ShowEBookInfo() const
	{
		ShowBookInfo();
		std::cout << "DRMKey : " << DRMKey << std::endl;
	}
	~Ebook() {}
};


int main(void)
{
	Book book("좋은 C++", "555-12345-890-0", 20000);
	Book book2 = book;

	book2.ShowBookInfo();

	Book book3("열혈 C++", "155-12345-890-0", 40000);
	Book book4("좋은 C++", "555-12345-890-0", 20000);

	book4 = book3;

	book4.ShowBookInfo();
}

 

1-2. C++ 기반의 데이터 입출력

예제 StablePointPtrArray.cpp 65, 66행을 다음과 같이 구성할 수 있도록,

- Point 클래스를 대상으로 연산자 오버로딩을 진행해보자.

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


물론, 실행결과에는 변함이 없도록 오버로딩 해야 한다.

#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);
	friend ostream& operator<<(ostream& os, const Point* pos);
};

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

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

 

  이번에는 재미삼아서 2차원 배열접근에 대한 연산자 오버로딩을 진행하고자 한다.
- 실제로 이렇게까지 연산자를 직접 오버로딩 하는 경우는 거의 없다.
- 다만, 필자는 호기심을 유발 및 충족시킨다는 측면에서 이 문제를 제시하는 것이다.
- 그러니 여러분도 이 문제에 단순한 호기심과 즐거움을 느꼈으면 좋곘다. 그럼 문제를 제시하겠다.
- 다음의 이름으로 클래스를 정의하자.

 

class BoundCheck2DIntArray{...}
이 클래스는 BoundCheckIntArray 클래스의 2차원 배열 버전이다.

따라서 다음과 같이 객체를 생성하면,
BoundCheck2DIntArray arr2d(3, 4);

세로와 가로의 길이가 각각 3 4, int 2차원 배열처럼 동작하는 arr2d 객체가 생성되어,
다음의 형태로 데이터를 저장 및 참조할 수 있어야 한다.

for (int n = 0; n < 3; n++)
        for (int m = 0; m < 4; m++)
               arr2d[n][m] = n + m;
 
for (int n = 0; n < 3; n++)
{
        for (int m = 0; m < 4; m++)
               cout << arr2d[n][m] << ' ';
        cout << endl;
}

 

- 참고로 두개의 []연산자를 동시에 오버로딩 하는 것은 허용되지 않기 때문에, 

- 위의 다음 문장은, arr2d[n][m] 두번의 []연산자 호출을 동반하게끔 구현해야 한다.
, 첫번째 []연산에 의해서 위의 문장은 다음과 같이 해석되어야 하며, (arr2d.operator[](n))[m];
그리고 arr2d.operator[](n)연산의 반환 값을 이용해서 두 번째[] 연산은 다음과 같이 해석되어야한다.
((반환 값).operator[])(m);

 

#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];
	}
	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;
	}
};
typedef BoundCheckIntArray* BoundCheckIntArrayPtr;

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

public:
	BoundCheck2DIntArray(int col, int row) : arrlen(col)
	{
		arr = new BoundCheckIntArrayPtr[col];
		for (int i = 0; i < col; i++)
			arr[i] = new BoundCheckIntArray(row);
	}
	BoundCheckIntArray& operator[] (int idx)
	{
		if (idx < 0 || idx >= arrlen)
		{
			cout << "Array index out of bound exception" << endl;
			exit(1);
		}
		return *(arr[idx]);
	}
	~BoundCheck2DIntArray() {
		for (int i = 0; i < arrlen; i++)
			delete arr[i];
		delete[] arr;
	}
};
int main(void)
{
	BoundCheck2DIntArray arr2d(3, 4);

	for (int n = 0; n < 3; n++)
		for (int m = 0; m < 4; m++)
			arr2d[n][m] = n + m;

	for (int n = 0; n < 3; n++)
	{
		for (int m = 0; m < 4; m++)
			cout << arr2d[n][m] << ' ';
		cout << endl;
	}
}

 

Comments