프로그램에서 초기화 작업
초기화 작업이란 프로그램이 작업을 시작하기 위해서 변수 등의 상태 값을 설정해주는 것을 말한다. 간단한 프로그램을 예를 들어보자.
#include <stdio.h>
int main()
{
int sum = 0, num = 0, i = 0;
while(i++ < 5){
printf("%dth : ", i);
scanf("%d", &num);
sum = sum + num;
}
printf("sum = %d\n", sum);
return 0;
}
위의 프로그램은 5개의 숫자를 입력받아 더한 결과값을 출력하는 프로그램이다. 'main' 함수의 첫번째 명령문과 같이 변수에 값을 대입하는 작업이 바로 초기화 작업이다. 위의 소스를 C++의 클래스 문법을 사용하여 변환해보겠다.
#include <stdio.h>
class My_Sum
{
private:
int m_sum;
public:
void Init()
{
m_sum = 0;
}
void AddNum(int num)
{
m_sum = m_sum + num;
}
int GetResult()
{
return m_sum;
}
};
int main()
{
int num = 0, i = 0;
My_Sum data;
data.Init();
while(i++ < 5){
printf("%dth : ", i);
scanf("%d", &num);
data.AddNum(num);
}
printf("sum : %d\n", data.GetResult());
return 0;
}
위의 소스에서는 'main' 함수에서 'My_Sum' 객체를 생성하고 해당 객체의 'Init' 함수를 호출하여 객체의 멤버 변수를 초기화 시켜준다. 여기서 생각할 수 있는 것은 'Init' 함수를 사용하지 않으면 멤버 변수를 초기화 할 수 없다는 점이다. 즉, 프로그램의 기능을 제대로 수행시키려면 'Init' 함수는 필수로 호출해야 한다. 이처럼 변수의 초기화를 위해서 'Init' 함수를 호출한다는 것은 불편할 수도 있다. 혹시나 깜빡하고 호출하지 않을 경우에는 쓰레기값이 담긴 변수에 값이 저장되어 원하지 않는 결과가 나올수도 있다.
객체 생성자(Constructor)
위와 같은 불편함을 해소하기 위해서 나온 것이 바로 객체 생성자이다. 객체 생상자란 객체가 생성될 때에 자동으로 수행되는 자동 호출 함수를 말한다. 특징으로 클래스의 이름과 동일한 이름을 가지며 객체 생성과 동시에 내부적으로 호출되는 함수이기 때문에 반환값이 존재하지 않는다. 간단한 예를 들어 확인해보자.
#include <stdio.h>
class MyObject
{
public:
MyObject()
{
printf("Init Object\n");
}
};
int main()
{
MyObject obj;
return 0;
}
위 소스에서는 'main' 함수에서 객체를 생성하는 작업만 존재한다. 하지만 이를 실행하면 아래와 같은 결과를 출력하는 것을 볼 수 있다.
앞서 설명한 것처럼 객체의 생성과 동시에 내부적으로 클래스의 이름과 같은 객체 생성자가 호출된 것이다. 이러한 객체 생성자의 특징을 사용하여 C++ 문법으로 변환시킨 소스를 수정하면 아래와 같다.
#include <stdio.h>
class My_Sum
{
private:
int m_sum;
public:
My_Sum()
{
Init();
}
void Init()
{
m_sum = 0;
}
void AddNum(int num)
{
m_sum = m_sum + num;
}
int GetResult()
{
return m_sum;
}
};
int main()
{
int num = 0, i = 0;
My_Sum data;
while(i++ < 5){
printf("%dth : ", i);
scanf("%d", &num);
data.AddNum(num);
}
printf("sum : %d\n", data.GetResult());
return 0;
}
달라진 점을 찾아보면 먼저 클래스의 이름과 같은 이름을 가진 객체 생성자가 추가 되었고 그 안에 작업 내용으로 'Init' 함수가 추가되었다. 그리고 'main' 함수의 'Init' 함수의 호출 구문이 없어졌는데, 이는 객체를 생성함과 동시에 호출되는 객체 생성자에 'Init' 함수가 존재하기 때문이다. 이렇게 수정하게 되면 사용자는 더 이상 직접 초기화를 위한 함수를 호출하지 않아도 된다.
객체 생성자의 함수 호버로딩
객체 생성자를 필수적으로 꼭 사용해야 하는 것은 아니다. 객체의 초기화 작업이 필요 없다면 객체 생성자를 생략해도 된다. 그리고 C++의 함수 오버로딩 기술을 적용하여 하나의 객체에 여러 개의 객체 생성자를 만들 수도 있다.
기본 생성자
객체 생성자는 클래스의 이름과 동일한 이름을 가진다고 했다. 함수 오버로딩을 적용하여 클래스의 이름과 같은 함수를 하나 더 선언하면 어떻게 될 지 예를 들어 확인해보겠다.
class MyObject
{
private:
int m_data;
public:
MyObject()
{
m_data = 0;
}
MyObject(int value)
{
m_data = value;
}
int GetValue()
{
return m_data;
}
};
위의 소스에서는 클래스의 이름과 같은 이름을 가진 객체 생성자가 두 개 선언되었다. 하지만 함수 오버로딩을 사용했기 때문에 매개 변수의 개수가 다른 것을 확인할 수 있다. 이렇게 객체 생성자에 함수 오버로딩을 적용하면 두 개 이상의 객체 생성자를 만들 수 있다. 이 때 매개 변수가 없는 객체 생성자를 기본 생성자(Default Constructor)라고 한다. 아래의 소스처럼 'MyObject' 객체를 아무런 추가 표현없이 그냥 생성하면 기본 생성자가 호출된다.
#include <stdio.h>
int main()
{
MyObject obj;
printf("obj = %d\n", obj.GetValue());
return 0;
}
기본 생성자가 아닌 다른 생성자 사용 방법
그렇다면 어떻게 객체를 생성해야 추가로 만든 객체 생성자를 사용할 수 있을까? 기본 생성자와 다른 객체 생성자의 차이는 매개 변수이다. 객체를 생성할 때에도 표현 방법으로 매개 변수 형식을 같이 적어주면 된다. 이렇게 되면 함수를 호출하는 표현과 비슷해지지만, 이는 객체를 선언하는 것이며 객체 생성자 중에서 전달해주는 매개 변수를 사용하는 객체 생성자를 호출하겠다는 의미이다. 아래 소스를 보며 확인해보자.
#include <stdio.h>
int main()
{
MyObject obj(10);
printf("obj = %d\n", obj.GetValue());
return 0;
}
객체 생성자를 사용하는 이유
객체 생성자를 사용하는 이유는 앞의 내용을 읽어보면 알 수 있듯이 사용자의 편리성을 위해서이다. 사용자가 직접 객체를 초기화하지 않아도 되는 편리함이 그 이유가 될 것이다. 그리고 또 하나의 이유는 안정성에 있다. 사용자가 실수로 객체의 멤버 변수를 초기화하지 않을 경우에 원하는 결과가 나오지 않는 상황이 생길 수 있다. 이런 실수를 객체 생성자가 사전에 막아주는 것이다. 객체 생성자를 잘 활용하는 습관을 들이는 것이 중요하다고 생각한다.
'C++ > C++ 기초' 카테고리의 다른 글
네임스페이스 (Namespace) (0) | 2019.09.09 |
---|---|
객체 파괴자 (0) | 2019.09.05 |
오버로딩(Overloading) (0) | 2019.09.01 |
클래스(Class) (0) | 2019.08.31 |
C++ 개요 (0) | 2019.08.31 |