본문 바로가기
PS/Data Structure

[자료구조] 두 개의 Rectangle 위치 비교(feat. C++)

by sebinChu 2022. 5. 20.

자료구조 수업 첫번째 과제는

 

1. Rectangle.cpp와 Rectangle.h, main.cpp의 관계를 파악하고

Rectangle 객체 생성 ~ 파괴 관계를 아는 것. 

 

2. 두 개의 Rectangle 객체 위치 비교.

 


 

첫과제의 1번을 해결하기 위해서 

1. 클래스와 객체

2. 생성자&파괴자

3. 연산자 오버로딩

의 개념에 대한 이해가 필요했다. (이에 대해서는 따로 C++ 카테고리에서 자세히 설명할 것이다.)

 

코드 해석을 하기 위해 가장 중요한 것은 "생성자와 소멸자가 언제 어떻게 생성되는지 이해하고, 그를 바탕으로 

프로그램 결과물을 파악" 하는 것이라고 느꼈다.

 

다음은 교수님께서 작성하신 main.cpp 내용이다. 

int main()
{
	Rectangle r1(1, 1, 3, 4);
	std::cout << r1;  //출력을 할 때 마다 연산자 오버로딩 때문에 OS 리턴 받아서 그대로 출력

	//동적으로 할당된 포인터 객체 r2 생성 
	Rectangle* r2 = new Rectangle(2, 3, 5, 5);
	std::cout << *r2;

	r1 = r1 + *r2; // 연산자 오버로딩을 통한 객체의 연산 

	//반환된 객체 출력 
	std::cout << r1;

	// 동적할당 해제
	delete r2;
}

r1 = r1 + *r2; 라는 코드는 연산자 오버로딩이 필수이다. 

왜냐하면 연산의 대상들은 내가 만든 임의의 객체이고, + 연산이 무엇을 수행하는지 모르기 때문에

연산자 오버로드를 통해 함수를 생성해줘야 한다. r1이 오버로드된 연산자 함수를 호출하고

*r2는 Rectangle& r이라는 뜻이다. r2는 위에서 포인터 객체로 생성해주었기에 호출도 이와 같은 맥락으로 해준다.

 

다음은 Rectangle.cpp의 출력 연산자 오버로딩 내용이다.

std::ostream& operator <<(std::ostream& os, Rectangle& r) {
	os << "height : " << r.height << std::endl
		<< "width : " << r.width << std::endl
		<< "x : " << r.xLow << std::endl
		<< "y : " << r.yLow << std::endl << std::endl;

	return os; //연산자 오버로딩을 통해 os를 반환함 == 출력함.
}

출력 연산자 오버로딩은 c++을 사용하다보면 자연스레 익힐 수 있는 것같다.

여기서 기억해야 할 점은 객체의 멤버 변수를 객체이름.멤버변수 방식으로 호출해주는 것이다.


 

 

첫과제의 2번은 두 Rectangle 객체의 위치 비교다. 

2개의 객체 r1, r2를 입력 연산자 오버로딩을 이용해 x, y, width, height 값을 런타임에 직접 입력 받고 

좌표평면 상 이 객체들이 겹치는지, 넓이는 얼마인지 구하면 된다. 

사각형이 존재하는 좌표의 기준은 사각형의 왼쪽 아래이다. (임의로 정한 것.)

 

두 개의 Rectangle 객체의 중첩을 확인하기 위해서는 아래와 같은 그래프와 함께 수학적 이해도 필요하다. 

경우의 수를 나누어서 코드를 작성해야 누적되는 부분/ 빠뜨린 부분을 놓지않고 검사할 수 있다. 

사각형의 각 꼭짓점은 좌표로서 표현된다. 오른쪽 위 좌표는 (x좌표 + 가로길이, y좌표 + 세로길이)로 표현된다.

 

직사각형 중첩에 대한 수학적 이해는 r1 사각형의 밑면(핑크색 부분)이 r2 사각형의 위쪽에 위치하면 겹치지 않고, 

r1 사각형의 왼쪽 변이 r2 사각형의 오른쪽 보다 더 오른쪽에 위치하면 겹치지 않고...의 사고를 통해 직사각형 네 점의 좌표를 그림에 나타난 바와 같이 새롭게 정의하였다.

 

모든 경우의 수를 검증하기 위해서는 겹치는 부분의 왼쪽 좌표와 오른쪽 좌표를 검증해주면 된다. (이해가 어려우면 직접 그림을 그려보면서 읽으면 된다.)

 

다음은 사각형이 겹치는 영역을 확인해주는 CheckOverlap 함수 구현 코드다.

Rectangle Rectangle::CheckOverlap(Rectangle& r1, Rectangle& r2) { //겹치는 영역 확인
	if (r1.width + r1.xLow <= r2.xLow ||
		r1.xLow >= r2.width + xLow ||
		r1.height + r1.yLow <= r2.yLow ||
		r1.yLow >= r2.height + r2.yLow)
	{
		Rectangle r3(0, 0, 0, 0); //겹치는 영역이 없을 때는 0,0,0,0 값을 갖는 r3 반환
		return r3;
	}

	//사각형이 겹칠 때 
	else {
		static int left_x = 0;  static int right_x = 0;  static int left_y = 0; static int right_y = 0;

		//겹치는 사각형의 왼쪽 아래 꼭짓점
		if (r1.xLow < r2.xLow) {
			left_x = r2.xLow;
		}
		else {
			left_x = r1.xLow;
		}
		if (r1.yLow < r2.yLow) {
			left_y = r2.yLow; 
		}
		else {
			left_y = r1.yLow;
		}

		//겹치는 사각형의 오른쪽 위 꼭짓점
		if (r1.xLow + r1.width < r2.xLow + r2.width) {
			 right_x = r1.xLow + r1.width;
		}
		else {
			 right_x = r2.xLow + r2.width;
		}
		if (r1.yLow + r1.height < r2.yLow + r2.height) {
			 right_y = r1.yLow + r1.height;
		}
		else {
			right_y = r2.yLow + r2.height;
		}

		//겹치는 부분의 가로, 세로 길이
		int overlap_h = right_y - left_y; 
		int overlap_w = right_x - left_x;

		Rectangle r3(left_x, left_y, overlap_h, overlap_w); //겹치는 영역의 값을 인자로 전달해서 반환.

		return r3; //겹치는 영역을 나타내는 객체 반환.
	}	
}

 

바깥 if 문의 조건 4가지는 위에서 설명한 겹치지 않는 영역 조건이다. 

이는 그림에 색칠된 각 변 중에 같은 색깔끼리 겹치는 영역을 확인하면 직관적인 이해가 가능하다. 

 

else 문은 사각형이 겹치는 경우이므로 겹치는 사각형의 좌표를 구해준다. 

else 문 밖에서 선언된 변수들을 else의 중첩 if 문 블록{ 안에서 } 사용하기 위해 static 선언을 해주었다.

 

(if 문 밖에서 선언된 변수를 if 문 내에서 사용하기 위해 더 좋은 방법이 있으면 댓글로 공유 부탁드립니다..!!)

 

두 사각형이 겹칠 때의 좌표를 else 문을 통해 구하고, 이 변수들을 Rectangle 클래스 생성사 함수에 전달하여  r3 객체를 만들고, 이를 반환한다. 

 

이 과제를 하면서 알게된 중요한 사실은

1. 함수 반환형에 객체도 반환이 된다는 것. 호출 시 반환형 자리에는 클래스명, 함수 return 시에는 객체명을 쓰면 된다.

2. 또한 함수 내부에서 생성된 객체를 반환한다고 해서 해당 함수에서 바뀐 변수의 값이 그대로 반환되지 않는다는 점이다. 이 부분은 객체 지향 프로그래밍을 할 때 특히나 실수가 잦을 수 있다는 생각이 들어, 더욱 구체적으로 공부해야겠다는 다짐을 하게됐다.

3. 클래스의 private 변수를 쓰기 위해서 변수 반환 함수를 쓰기도 한다는 것이다.

댓글