본문 바로가기

Programming Languages/C++

[클래스와 객체]-생성자와 소멸자-생성자

객체의 속성(즉, 데이터 멤버)은 객체의 현재 상태를 나타내는 값이다. 그러므로 처음 객체가 만들어졌을 때에는 그 객체의 초기 상태를 적절히 지정하여 두어야 한다. 

반드시 수행해야 하는 초기화 과정을 실수로 누락하면 프로그램은 올바로 동작하지 않는다. 이러한 실수가 발생하지 않도록 초기화 과정을 자동화할 수 있다면 불필요한 에러를 유발하지 않을 수 있다. 이러한 목적으로 사용할 수 있는 것이 생성자(constructor)이다. 생성자는 객체가 생성될 때 수행할 작업을 정의하는 특수한 멤버함수로서, 객체를 정의하는 문장에 의해 자동적으로 호출된다.

생성자의 선언

생성자의 기본적인 선언 형식은 다음과 같다.

class ClassName{
  ......
public:
  ClassName(fParameterList){ // 생성자
    ......
  }
  ......
};

생성자는 기본적으로 일반 멤버함수와 유사하다. 매개변수를 통해 인수를 전달받을 수 있으며, 여러 가지 방법으로 사용될 수 있도록 생성자를 다중정의할 수도 있다. 그러나 다음과 같이 생성자만의 고유한 사항도 있다.

  1. 생성자는 클래스의 이름을 사용하여 선언한다.
  2. 생성자의 머리부에는 반환 자료형을 표시하지 않는다. 또한 몸체 내에서 return 명령으로 값을 반환할 수 없다(이 경우 일반 함수에서는 머리부의 반환 자료형에 void라고 표기하나. 생성자에서는 반환 자료형을 표기하지 않음).
  3. public으로 선언한 생성자만 클래스 외부에서 객체를 만드는 데 사용할 수 있다.

초기화 리스트

생성자가 하는 주요 작업으로는 데이터 멤버에 적절한 초깃값을 넣는 것이다. 생성자에서 데이터 멤버의 값에 초깃값을 대입하는 것은 초기화 리스트(initializer list)를 이용하여 간결하게 표현할 수 있다.

초기화 리스트는 함수의 머리에 콜론 ( : )을 기입하과 '변수명[초깃값]' 또는 '변수명(초깃값)' 형태로 초깃값을 지정한 것이다. 만일 초기화 항목이 여러개 있을 때에는 쉼표( , )로 구분한다.

데이터 멤버의 값을 초기화하는 간단한 작업의 경우 초기화 리스트를 통해 처리하는 것이 일반적이다. 초기화와 대입은 결과적으로 동일한 것처럼 보이지만, 실제로는 차이가 있다. 초기화는 그 대상이 만들어질 때 값이 지정되는 것이고, 대입 명령은 이미 만들어져 있는 대상에 값을 넣는 것이다. 예를 들어 const로 지정된 상수는 초기화를 통해 값을 지정할 수는 있지만, 대입 명령을 이용하여 상수에 값을 지정할 수는 없다.

[소스코드 4-6]의 CounterM 클래스는 최댓값이 제한된 계수기를 만들기 위한 것이다. 매개변수를 통해 전달되는 최댓값까지 계수를 한 후 다시 계수를 하면 0으로 돌아가도록 count() 함수를 정의한 것을 볼 수 있다.

// 소스코드 4-6 : CounterM.h

#pragma once
#ifndef COUNTERM_H_INCLUDED
#define COUNTERM_H_INCLUDED

class CounterM {   // 클래스 CounterM의 선언 시작
	const int maxValue;   // 계수기의 최댓값
	int value;   // private 데이터 멤버
public:   // public 멤버함수
	CounterM(int mVal) : maxValue{mVal}, value{0} {}   // 생성자
	void reset() {   // 계수기의 값을 0으로 지움
		value = 0;
	}
	void count() {   // 계수기의 값을 1 증가시킴
		value = value < maxValue ? value + 1 : 0;
	}
	int getValue() const {   // 계수기의 현재 값을 반환함
		return value;
	}
};
#endif // !COUNTERM_H_INCLUDED

const int maxValue;는 maxValue라는 데이터 멤버를 선언하여 계수기 객체의 최댓값을 저장하게 하였는데, 각각의 계수기 객체의 최댓값은 객체가 생성된 후 변하지 않도록 하기 위해 const를 지정하고 있다.

CounterM 생성자는 계수기의 최댓값을 매개변수를 통해 전달받는데, 초기화 리스트를 사용하여 이 값으로 maxValue라는 const 멤버를 초기화하는 것을 볼 수 있다. 그러나 만일 위의 CounterM 생성자 대신

CounterM(int mVal){
   value = 0;
   maxValue = mVal;   // ①
}

와 같이 생성자를 선언하면 문법 에러가 발생할 것이다. maxValue가 const로 지정되어 있어서 ①과 같이 값을 대입할 수 없기 때문이다. 그러므로 이러한 경우는 초기화 리스트를 사용하여야 한다.

[소스코드 4-7]은 CounterM 클래스의 객체를 생성하여 사용하는 예를 보여주는 프로그램이다.

// 소스코드 4-7 : CntMMain.cpp

#include <iostream>
#include "CounterM.h"
using namespace std;

int main() {
	CounterM cnt(9);   // Counter 객체의 정의

	cout << "계수기의 현재 값:" << cnt.getValue() << endl;
	for (int i = 0; i < 12; i++) {
		cnt.count();   // 계수기를 1 증가시킴
		cout << "계수기의 현재 값:" << cnt.getValue() << endl;
	}
	return 0;
}

====== 결과 ======
계수기의 현재 값:0
계수기의 현재 값:1
계수기의 현재 값:2
계수기의 현재 값:3
계수기의 현재 값:4
계수기의 현재 값:5
계수기의 현재 값:6
계수기의 현재 값:7
계수기의 현재 값:8
계수기의 현재 값:9
계수기의 현재 값:0
계수기의 현재 값:1
계수기의 현재 값:2

CounterM 클래스의 생성자는 1개의 int 값이 인수로 전달되어야 하므로 CounterM cnt(9); 와 같이 객체를 선언할 때 이에 해당되는 실 매개변수를 포함해야 한다. 반복문을 사용하여 12회 계수를 하며 계수기의 값을 출력한다. 최댓값인 9까지 계수하고 난 후 다시 계수를 하면 0으로 리셋되는 것을 볼 수 있다.