CPP Module 02

hhkim·2022년 5월 16일
0

42cursus

목록 보기
11/20
post-thumbnail

Orthodox Canonical Form

Orthodox 규정을 따르면서 개발할 것
(아래 네 가지 형태를 명시적으로 정의하여 선언할 것)
1. 기본 생성자
2. 복사 생성자
3. 복사 대입 연산자
4. 소멸자

class A
{
  A();
  ~A();
  A(const A &a);
  A &operator=(const A &a);
};

복사 생성자

같은 클래스 타입의 객체에 대한 참조를 인자로 전달받아, 그 참조를 가지고 자신을 초기화하는 방법

  • 정의하지 않으면 디폴트 복사 생성자가 생성됨
  • 디폴트 복사 생성자는 포인터를 복사하는 얕은 복사 (주소 복사)
  • 복사 생성자는 원본 객체의 내용을 복사하여 새로운 객체를 생성하는 깊은 복사 (값 복사)
  • 아래와 같은 상황에서 사용됨
    • 객체가 함수에 인자로 전달될 때
    • 함수가 객체를 반환할 때
    • 새로운 객체를 같은 클래스 타입의 기존 객체와 똑같이 초기화할 때
A::A(const A &a) {
	*this = a;  // 오버로딩된 할당 연산자 호출
}
A origin;			//  기본 생성자 호출
A copied(origin);	//  복사 생성자 호출

복사 대입 연산자

대입(할당) 연산자 오버로딩
기존 대입 연산자(=)를 재정의

  • 디폴트 대입 연산자는 얕은 복사
    👉 깊은 복사를 하도록 대입 연산자 오버로딩
  • operator 키워드와 연산자(=)를 묶어서 함수의 이름을 정의
  • 두 객체가 모두 생성 및 초기화되었고, 연산자 오른쪽에 해당 타입의 객체가 들어왔을 때만 복사 대입 연산자 사용 (새 객체의 생성이 필요하지 않은 경우)
A &A::operator=(const A &a) {
	// 값 복사 실행
	return (*this);
}
A a1;		// 기본 생성자 호출
A a2 = a1;	// 복사 생성자 호출
A a3;		// 기본 생성자 호출
a3 = a2;	// 복사 대입 연산자 사용

깊은 복사를 하는 이유

  • 생성자 내에서 동적 할당을 하는 경우, 객체 소멸 과정에서 중복으로 delete를 하게 됨
  • 원래 가리키던 메모리의 포인터를 삭제하지 않고 잃어버리게 되어 메모리 누수 발생

ex00 - My First Class in Orthodox Canonical Form

구현

고정 소수점, 부동 소수점 등 구현은 뒤에서 하기 때문에 여기서는 클래스를 OCF에 맞춰서 생성하기만 하면 됨

Fixed 클래스

private 멤버

  • integer 멤버 변수: 고정 소수점 숫자
  • static constant integer 멤버 변수: 소수부를 표현할 비트 수. 항상 8

public 멤버

  • 기본 생성자: 고정 소수점 숫자를 0으로 초기화
  • 복사 생성자
  • 복사 대입 연산자 오버로딩
  • 소멸자
  • 멤버 함수 int getRawBits( void ) const;: 고정 소수점 값의 raw 값 리턴
  • 멤버 함수 void setRawBits( int const raw );: 고정 소수점 수의 raw 값 설정

테스트

int	main(void) {
	Fixed a;
	Fixed b(a);
	Fixed c;

	c = b;

	std::cout << a.getRawBits() << std::endl;
	std::cout << b.getRawBits() << std::endl;
	std::cout << c.getRawBits() << std::endl;

	return (0);
}
$> ./a.out
Default constructor called
Copy constructor called
Copy assignment operator called
getRawBits member function called
Default constructor called
Copy assignment operator called
getRawBits member function called
getRawBits member function called
0
getRawBits member function called
0
getRawBits member function called
0
Destructor called
Destructor called
Destructor called
$>

ex01 - Towards a more useful fixed-point number class

개념

고정 소수점(Fixed point)

소수부를 표현할 비트가 고정되어 있는 방식

  • 부호 비트, 정수부, 소수부로 나뉨
  • 구현하기 편리하고 속도가 빠름
  • 표현 가능한 수의 범위가 한정되어 있기 때문에 정밀도가 낮음

부동 소수점(Floating point)

2진수 변환 후 정수부에 1만 남을 때까지 소수점을 왼쪽으로 이동시키는 정규화 과정을 거치는 방식

  • IEEE 표준에서는 실수를 저장할 때 32비트(float) 또는 64비트(double)를 사용
  • 부호 비트, 지수부, 가수부로 나뉨
  • 정규화 과정을 거치면 1.xxx... * 2^n
    지수(n)에 bias를 더한 값을 지수부에 저장 (지수의 부호를 표현하기 위해 필요)
    bias는 32비트 기준에서 지수부가 8비트이므로 2^(8 - 1) - 1 = 127
  • 가수(xxx...)는 가수부(23비트)에 그대로 저장 (정규화 후 1.11101 * 2^2)면 가수부는 11101)

구현

허용 함수

<cmath>roundf()
인자로 주어진 수를 반올림하여 리턴

float roundf (float x);

Fixed 클래스

public 생성자

  • const int를 인자로 받는 생성자: 인자로 받은 수를 고정 소수점 값으로 변환
  • const float를 인자로 받는 생성자: 인자로 받은 수를 고정 소수점 값으로 변환

public 멤버 함수

  • float toFloat( void ) const;: 고정 소수점 값을 부동 소수점 값으로 변환
  • int toInt( void ) const;: 고정 소수점 값을 정수로 변환

연산자 오버로딩

  • 클래스 파일에 추가 (클래스 내부가 아님)
  • 삽입 연산자(<<)를 고정 소수점 값의 부동 소수점 표현을 매개 변수로 전달된 출력 스트림에 삽입
    👉 toFloat() 호출

고정 소수점 - 부동 소수점 변환

서브젝트의 고정 소수점의 소수부 표현 비트는 8비트
👉 32 비트 체계에서 앞의 24비트를 정수로, 뒤의 8비트를 소수로 표현

정수를 고정 소수점으로 변환

  • << 연산으로 8비트의 빈 공간을 만듦
    예) n << 8

실수를 고정 소수점으로 변환

  • float, double 등 부동 소수점의 경우 << 같은 쉬프트 연산자를 사용할 수 없음
    👉 우회하여 표현 (정수 1에 쉬프트 연산을 한 256을 곱하는 것)
    👉 실수를 정수로 변환하는 것이 아니라 비트 자체를 정수로 간주하고 처리하는 것!!
  • 고정 소수점 값을 저장할 클래스 멤버 변수가 int이므로 (남은 소수부를 없애고 + 좀더 정확한 연산을 위해) 허용 함수인 roundf를 사용하여 반올림
    예) roundf(n * (1 << 8))

고정 소수점을 부동 소수점으로 변환

  • 저장된 고정 소수점 값(int)을 float으로 변환한 뒤 << 연산한 값을 다시 되돌림
    예) static_cast<float>(n) / (1 << 8)

테스트

int	main(void) {
	Fixed		a;
	Fixed const	b(10);
	Fixed const	c(42.42f);
	Fixed const	d(b);
	
	a = Fixed(1234.4321f);

	std::cout << "a is " << a << std::endl;
	std::cout << "b is " << b << std::endl;
	std::cout << "c is " << c << std::endl;
	std::cout << "d is " << d << std::endl;

	std::cout << "a is " << a.toInt() << " as integer" << std::endl;
	std::cout << "b is " << b.toInt() << " as integer" << std::endl;
	std::cout << "c is " << c.toInt() << " as integer" << std::endl;
	std::cout << "d is " << d.toInt() << " as integer" << std::endl;

	return (0);
}
$> ./a.out
Default constructor called
Int constructor called
Float constructor called
Copy constructor called
Copy assignment operator called
Float constructor called
Copy assignment operator called
Destructor called
a is 1234.43
b is 10
c is 42.4219
d is 10
a is 1234 as integer
b is 10 as integer
c is 42 as integer
d is 10 as integer
Destructor called
Destructor called
Destructor called
Destructor called
$>

ex02 - Now we’re talking

구현

연산자 오버로딩

멤버 함수로 구현

  • 비교 연산자 > < >= <= == !=
  • 산술 연산자 + - * /
  • 증감 연산자 전/후위 ++, 전/후위 --
    1이 증감하는 게 아니라 표현 가능한 가장 작은 수를 증감
    👉 고정 소수점 정수에서 1을 증감하고 그걸 다시 부동 소수점으로 바꾸면 될 듯

public 멤버 함수

비교 연산자 오버로딩 활용

  • static min(): 2개의 고정 소수점 수에 대한 참조를 받아서 작은 것 리턴
  • static min(): 2개의 const 고정 소수점 수에 대한 참조를 받아서 작은 것 리턴
  • static max(): 2개의 고정 소수점 수에 대한 참조를 받아서 큰 것 리턴
  • static max(): 2개의 const 고정 소수점 수에 대한 참조를 받아서 큰 것 리턴

테스트

int	main(void) {
	Fixed		a;
	Fixed const	b(Fixed(5.05f) * Fixed(2));

	std::cout << a << std::endl;
	std::cout << ++a << std::endl;
	std::cout << a << std::endl;
	std::cout << a++ << std::endl;
	std::cout << a << std::endl;

	std::cout << b << std::endl;

	std::cout << Fixed::max(a, b) << std::endl;

	return (0);
}
$> ./a.out
0
0.00390625
0.00390625
0.00390625
0.0078125
10.1016
10.1016
$>

ex03 - BSP

개념

BSP(Binary Space Partitioning)

이진 공간 분할법

  • 다각형을 재귀적으로 분할해서 트리를 생성하는 알고리즘
  • 게임 맵을 생성할 때 쓰인다고 함

👉 서브젝트에서는 점이 삼각형 안에 있는지만 판단하면 되기 때문에 굳이 트리까지 생성할 필요는 없을 듯

점의 다각형 내부/외부 판별

  • 점을 오른쪽으로 늘려 직선을 그었을 때 다각형과 만나는 점의 개수를 셈
    👉 짝수면 외부, 홀수면 내부
  • 다각형의 두 꼭짓점 y 좌표 사이에 점의 y 좌표가 위치하지 않으면 무조건 외부
  • 점의 x 좌표보다 교점의 x 좌표가 더 커야 함 (오른쪽으로 직선을 늘리기 때문에)
  • 선분의 꼭짓점 A, B와 점 P가 있을 때 선분과 점 P를 늘린 직선의 교점의 x 좌표를 구하는 공식
    (B.xA.x)(P.yA.y)/(B.yA.y)+A.x(B.x - A.x) * (P.y - A.y) / (B.y - A.y) + A.x

구현

주어진 점이 삼각형 안에 있는지 아닌지 판단하기

Point 클래스

private 멤버

  • Fixed const x
  • Fixed const y
  • 다른 유용한 모든 것

public 멤버

  • 기본 생성자: x, y0으로 초기화
  • 2개의 const float을 받아서 x, y를 초기화하는 생성자
  • 복사 생성자
  • 복사 대입 연산자 오버로딩
  • 소멸자
  • 다른 유용한 모든 것

복사 대입 연산자를 오버로딩할 때 const 값을 리턴하지 않아 발생하는 오류

Fixed 클래스에서 = 연산자를 오버로딩할 때 Fixed & 형을 반환하기 때문에, Point의 복사 대입 연산자를 오버로딩하면서 _x = point.getX();와 같이 사용할 수 없음
👉 const_cast<T>const를 일시적으로 제거

bsp.cpp

bool bsp( Point const a, Point const b, Point const c, Point const point);
  • a, b, c: 삼각형의 꼭짓점
  • point: 확인할 점
  • 리턴값
    • true: 점이 삼각형 안에 있는 경우
    • false: 그렇지 않은 경우 (삼각형의 꼭짓점이나 변에 있는 경우 포함)
  1. 삼각형의 각 꼭짓점을 기준으로 한 꼭짓점과 다음 꼭짓점에 대해 반복 (a-b, b-c, c-a)
  2. 점이 두 꼭짓점의 y 좌표 사이에 있는지 확인
  3. 사이에 있는 경우 교점의 x 좌표 찾기
  4. 교점의 x 좌표가 점의 x 좌표보다 크면 교점의 개수 증가
  5. 반복이 끝나면 교점의 개수로 내부/외부 판별

0개의 댓글