[C++] 클래스(3)-생성자와 소멸자

김형태·2021년 5월 6일
0

C++

목록 보기
5/13
post-custom-banner

지금까지는 객체를 생성하고 객체의 멤버변수 초기화를 목적으로 'init어쩌구'라는 이름의 함수를 정의하고 호출했다. 정보 은닉을 목적으로 멤버변수들을 private으로 선언했으니 이는 어쩔 수 없다. 다행히 '생성자'라는 것을 이용하면 객체도 생성과 동시에 초기화할 수 있다.

1. 생성자(Constructor)

Class SimpleClass
{
private:
    int	num;
public:
    simpleClass(int n) // 생성자
    {
       num = n;
    }
    int	getNum() const
    {
    	return (num);
    }
}

//생성자 라는 주석이 달린 함수를 보자.

  • 클래스의 이름과 함수의 이름이 동일하다.
  • 반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다.

이러한 유형의 함수를 가리켜 '생성자(constructor)'라고 하며, 이는 객체 생성 시 딱 한번 호출된다. 이전에 생성자를 정의하지 않았을 때에는 다음과 같이 객체를 생성했다.

SimpleClass sc;

이제 생성자가 정의되었으니, 객체 생성 과정에서 자동으로 호출되는 생성자에게 절달할 인자의 정보를 추가해야 한다.

SimpleClass sc(20);

생성자의 특징은 다음과 같다.

  • 생성자도 함수의 일종이니 오버로딩이 가능하다.
  • 생성자도 함수의 일종이니 매개변수에 '디폴트 값'을 설정할 수 있다.

다음과 같은 경우는 조심하자.

class	SimpleClass
{
private:
    int	num1;
    int num2;
public:
    SimpleClass() // 생성자1
    {
    	num1 = 0;
        num2 = 0;
    }
    SimpleClass(int n) // 생성자2
    {
    	num1 = n;
        num2 = 0;
    }
    SimpleClass(int n1, int n2) // 생성자3
    {
    	num1 = n1;
        num2 = n2;
    }
    SimpleClass(int n1 = 0, int n2 = 0) // 생성자4
    {
    	num1 = n1;
        num2 = n2;
    }
}

위와 같은 클래스의 생성자를 다음과 같이 호출하면

SimpleClass sc(10);

컴파일 에러가 뜰 것이다. 이유는 위와 같이 생성자 호출 시, 생성자2와 생성자4를 구분할 수 없기 때문이다!

다음과 같은 호출도 불가능하다.

SimpleClass sc();

SimpleClass sc(void); 라는 함수의 원형을 지역적으로 선언했을 경우, 둘을 구분할 수 없기 때문이다. 그러므로 매개변수가 없는 생성자는

SimpleClass sc;

와 같이 호출하자.

1.1. 멤버 이니셜라이저(Member Initializer)

1.1.1 멤버 이니셜라이저를 이용한 멤버 초기화

class Point
{
private:
    int x;
    int y;
public:
    Point(const int &xpos, const int &ypos) // 생성자
    {
        x = xpos;
        y = ypos;
    }
    ...
}

class Rectangle
{
private:
    Point upLeft;
    Point lowRight;
public:
    Rectangle(const int &x1, const int &y1, const int &x2, const int &y2);
    ...
}

Rectangle 클래스는 두 개의 Point 객체를 멤버변수로 가지고 있다. Rectangle 클래스의 생성자는 직사각형을 이루는 두 점의 정보를 직접 전달할 수 있게 정의했다. 이 정보를 통해서 두 개의 Point 객체를 초기화해야 한다. 이때 멤버 이니셜라이저를 사용할 수 있다.

Rectangle::Rectangle(const int &x1, const int &y1, const int &x2, const int &y2): upLeft(x1, y1), lowRight(x2, y2)
{
}

이때 선언부의 : upLeft(x1, y1), lowRight(x2, y2)가 바로 '멤버 이니셜라이저'이다. 이것이 의미하는 바는 다음과 같다.

  • 객체 upLeft의 생성과정에서 x1과 y1을 인자로 전달받는 생성자를 호출하라.
  • 객체 lowRight의 생성과정에서 x2과 y2을 인자로 전달받는 생성자를 호출하라.

이렇듯 멤버 이니셜라이저는 멤버변수로 선언된 객체의 생성자 호출에 활용된다.

1.1.2. 멤버 이니셜라이저를 이용한 변수 및 const 상수(변수) 초기화

멤버 이니셜라이저를 이용해서 객체가 아닌 멤버 변수도 초기화를 할 수 있다.

멤버 변수로 int num1, num2를 가지고 있는 Simple이라는 클래스가 있다고 생각해보자.

Simple(int n1, int n2): num1(n1)
{
	num2 = n2;
}

위에서 num1과 num2는 다음처럼 초기화 된다.

// 멤버 이니셜라이저를 이용한 초기화
int num1 = n1; 

// 생성자의 몸체 부분에 의한 초기화
int num2;
num2 = n2;

이렇게 일반적인 변수는 멤버 이니셜라이저가 아니어도 초기화가 가능하다. 그러나 멤버 변수가 const이거나 참조자라면 선언과 동시에 초기화가 이뤄져야 한다. 그러므로 멤버 변수가 const이거나 참조자라면 멤버 이니셜라이저를 통해 초기화할 수 있다.

1.2. 디폴트 생성자(Default Constructor)

메모리 공간의 할당 이후에 생성자의 호출까지 완료되어야 '객체'라고 할 수 있다. 즉, 객체가 되기 위해서는 반드시 하나의 생성자가 호출되어야 한다. 그리고 이러한 기준에 예외를 두지 않기 위해,

생성자를 정의하지 않는 클래스에서는 C++ 컴파일러에 의해 디폴트 생성자라는 것이 자동으로 삽입된다. 그런데 디폴트 생성자는 인자를 받지 않으며, 내부적으로 아무 일도 하지 않는 생성자이다.
SimpleClass() {}

따라서 디폴트 생성자를 선언 및 정의하지 않은 상태에서 인자를 받는 생성자가 선언 및 정의되어 있으면 디폴트 생성자가 삽입되지 않는다.


2. 소멸자

객체 생성 시 반드시 호출되는 것이 생성자라면, 객체 소멸 시 반드시 호출되는 것은 소멸자이다. 소멸자는 다음의 형태를 갖는다.

  • 클래스 이름 앞에 '~'가 붙은 형태의 이름은 갖는다.
  • 반환형이 선언되어 있지 않으며, 실제로 반환하지 않느낟.
  • 매개변수는 void형으로 선언되어야 하기 때문에 오버로딩도, 디폴트값 설정도 불가능하다.

예를 들어 AAA라는 클래스가 정의되어 있다면,
~AAA() { ... } 의 형태를 갖춘 것이 소멸자이다.

직접 소멸자를 정의하지 않으면, 디폴트 생성자와 마찬가지로 아무런 일도 하지 않는 디폴트 소멸자가 자동으로 삽입된다.

소멸자는 대개 생성자에서 할당한 리소스의 소멸에 사용된다. 예를 들어 생성자 내에서 new 연산자를 이용해서 할당해 놓은 메모리 공간이 있다면, 소멸자에서는 delete 연산자를 이용해서 이 메모리 공간을 소멸시켜야 한다.

profile
steady
post-custom-banner

0개의 댓글