CPP Module02
이 과제 이후부터는 Coplien’s Form을 맞춰서 클래스를 작성해야 함
- 기본 생성자
- 복사 생성자
- 할당 연산자 오버로딩
- 소멸자
이 4가지는 클래스에 항상 정의되어야 함
ex00
Fixed 클래스의 기본 틀을 작성하는 문제 서브젝트에 제시된 메인 파일을 보고 해당 결과와 동일하게 출력되도록 구성합니다.
기본 생성자
생성자 이름은 클래스와 이름이 같아야 합니다. 생성자는 리턴 타입이 없습니다.(void 아님)
기본 생성자의 경우 매개 변수를 갖지 않거나 모두 기본값이 설정된 매개 변수를 가지고 있습니다. 클래스를 인스턴스화할 때 사용자가 초기 값을 제공하지 않으면 기본 생성자가 호출됩니다.
일반적으로 생성자에서는 멤버 변수나 사용할 자원을 초기화 합니다.
class Point
{
private:
int _x;
int _y;
public:
Point()
{
_x = 0;
_y = 0;
} // 멤버 변수에 값을 할당 합니다.
Point(int x = 0, int y = 0): _x(x), _y(y)
{} // 멤버 변수에 값을 초기화 합니다.
}
복사 생성자
복사 생성자는 다음과 같은 형태를 가집니다.
Point(const Point &ref)
상수로 선언된 참조자이며, type은 클래스(자기 자신)이 되는 것입니다. 복사 생성자는 기존 값을 복사해 전달해주는 개념이라 값이 바뀌어서는 안됩니다. 따라서 const로 선언됩니다.
참조자를 사용하는 이유에 대해서 알기 전에 복사 생성자의 호출에 대해서 알아야 합니다.
복사 생성자의 호출은 다음 상황에서 발생합니다.
- 대입 연산
- 매개 변수로 들어갈 때
- 반환 값이 있을 때
위의 3가지 상황에서 참조를 통해 넘기게 되면 생성자 호출, 복사 생성자 호출을 안합니다. 만약 참조자가 아닌 그냥 매개변수로 받게 된다면 A(const A ref)는 새로운 A ref에 대해 복사 생성자가 호출되기 때문에 무한루프에 빠지게 됩니다.
또한 복사 생성자를 명시적으로 정의하지 않으면 멤버 대 멤버 복사를 진행하는 디폴트 복사 생성자가 묵시적으로 호출됩니다.
참고
https://wikidocs.net/14030 http://blog.naver.com/PostView.nhn?blogId=vgb910526&logNo=220665266599
할당 연산자 오버로딩
다음과 같이 선언합니다.
class MyClass
{
public:
...
MyClass & operator=(const MyClass &ref);
...
}
= 연산자가 우측에 할당한 내용을 상수 참조로 받는 점을 확인할 수 있습니다. 이렇게 하는 이유는 할당 연산자 왼쪽에 있는 내용을 바꾸는 것이지 오른쪽의 값의 변경은 원하지 않기 때문입니다.
가상의 MyClass 할당 연산자는 다음과 같이 작성할 수 있습니다.
MyClass & MyClass::operator=(const MyClass &ref)
{
...
return *this //자기 자신의 참조를 반환
}
this는 이 객체에 대한 포인터이며 멤버 함수로 호출되는 대상입니다. a = b는 a.operator=(b)처럼 취급되므로 this는 a입니다. 함수는 객체에 대한 포인터를 반환하는 것이 아니라 객체에 대한 참조를 반환해야 합니다. 그래서 *this를 반환했는데 이 코드는 무엇을 가리키고 있는지를 반환했지 포인터 자체를 반환한 것이 아닙니다. *this의 경우 컴파일러에 의해 참조로 변환됩니다.
할당 연산자에서 가장 중요한 것으로 자기 할당을 확인해야 합니다. 일반적으로 할당 연산자의 연산은 다음과 같습니다.
- MyClass가 내부적으로 가진 모든 메모리를 할당 해제합니다.
- ref의 내용을 보관하기 위해 메모리에 할당합니다.
- ref로부터 값을 인스턴스에 복사합니다.
- *this를 반환합니다.
MyClass mc;
...
mc = mc;
다음과 같은 코드에서 가장 먼저 mc가 내부적으로 들고 있는 모든 메모리를 해제합니다. 하지만 우측에도 mc이기 때문에 연산자는 엉망이 됩니다.
이런 문제를 손쉽게 피하기 위해 자기 할당을 확인합니다. 간단하게는 두 객체의 주소가 동일한지 확인하기만 해도 됩니다.
MyClass& MyClass::operator=(const MyClass &ref)
{
if (this != &ref)
{
... // 할당 해제, 값 복사
}
return *this
}
정리하자면 할당 연산자를 위한 가이드는 다음과 같습니다.
- 인자는 상수 참조로 받습니다.(할당 우측 값)
- 좌측에 참조를 반환해서 안전하고 적절한 연산자 연결을 지원합니다.(*this를 반환하는 방법으로)
- 포인터를 비교해서 자기 할당을 확인합니다.(this와 &ref)
참고
https://edykim.com/ko/post/c-operator-overloading-guidelines/
소멸자
객체가 제거될 때 호출됩니다.
~Fixed(void)
와 같은 형태로 클래스 이름 앞에 ~를 붙이면 됩니다.
소멸자는 인수와 반환 값이 없습니다.
이러한 규칙으로 인해 소멸자는 클래스 당 하나밖에 존재할 수 없습니다.
또한 소멸자를 명시적으로 호출하는 경우도 없습니다.
객체가 제거되기 전에 호출되기 때문에 다른 리소스를 사용한 객체라면 할당 해제를 하거나 기타 유지보수 작업을 하기에 적절합니다.
RAII(Resource Acquistion Is Initialization)
객체의 수명과 리소스 관리와 연관있는 프로그래밍 디자인 기법입니다. cpp에서는 클래스의 생성자와 소멸자로 구현되어 있습니다.
- 리소스(메모리, 파일 또는 DB 핸들)는 일반적으로 객체의 생성자에서 획득
- 해당 리소스는 객체가 살아있는 동안 사용할 수 있음
- 객체가 소멸하면 자원이 소멸
RAII의 장점은 리소스를 보유한 객체가 자동으로 정리됨에 따라 리소스 누수를 방지하는 데 도움이 된다는 것입니다.
ex01
서브젝트에서 제공한 문서(부동 소수점 및 고정 소수점 표현 방식)를 읽고 Fixed 클래스를 실제로 완성해보는 문제입니다.