26-2.여러 가지 생성자

26-2-가.디폴트 생성자

디폴트 생성자(또는 기본 생성자라고도 한다)란 인수를 가지지 않는 생성자이다. 생성자는 오버로딩이 가능하므로 여러 개를 둘 수 있는데 그 중 인수가 없는 생성자를 디폴트 생성자라고 부른다. 즉 인수 목록이 void인 생성자인데 Position 클래스의 경우 디폴트 생성자의 원형은 Position()이 된다. 다음 예제의 Position 클래스는 디폴트 생성자 하나만 정의하고 있다.

 

  : DefConstructor

#include <Turboc.h>

 

class Position

{

private:

     int x;

     int y;

     char ch;

 

public:

     Position() {

          x=0;

          y=0;

          ch=' ';

     }

     void OutPosition() {

          if (ch != ' ') {

              gotoxy(x, y);

               putch(ch);

          }

     }

};

 

void main()

{

     Position Here;

    

     Here.OutPosition();

}

 

디폴트 생성자는 호출부에서 어떤 값으로 초기화하고 싶은지를 전달하는 수단인 인수가 없다. 인수를 받아들이지 않기 때문에 객체의 멤버에 의미있는 어떤 값을 대입하지는 못하며 주로 모든 멤버를 0이나 -1 또는 NULL이나 빈문자열로 초기화한다. 여기서 0이라는 값은 실용적인 의미를 가지는 값이라기보다는 단순히 아직 초기화되지 않았음을 분명히 표시하는 역할을 한다. 어떤 값인지 알지도 못하는 쓰레기값보다는 그래도 0이라도 대입해 놓는 것이 더 나은데 이렇게 하면 멤버 함수에서 이 값을 사용하기 전에 초기화 되어 있는지를 점검할 수 있기 때문이다.

 

if (ptr == NULL) { ... }

if (value == 0) { ... }

 

디폴트 생성자가 포인터 변수를 NULL로 초기화해 놓으면 멤버 함수가 이 변수를 사용하기 전에 NULL인지 조사해 보고 NULL이면 그때 초기화를 할 수 있다. 즉 디폴트 생성자의 임무는 쓰레기를 치우는 것이며 멤버의 초기화는 이 멤버를 사용하는 멤버 함수가 호출될때까지 연기된다. 위 예제의 Position() 디폴트 생성자는 x, y는 0으로 초기화하고 ch에는 공백 문자를 대입하며 OutPosition 함수는 ch가 공백 문자를 가질 때 이 객체가 아직 초기화되지 않은 것으로 판단하고 문자 출력을 하지 않는다. 디폴트 생성자가 있는 객체를 선언할 때는 다음과 같은 여러 가지 방법을 사용할 수 있다.

 

① Position Here;

② Position Here=Position();

③ Position *pPos=new Position;

④ Position *pPos=new Position();

⑤ Position Here();

 

①번 형식이 가장 간단하며 예제에서 사용한 방법이다. 생성자에게 전달할 인수가 없으므로 타입 다음에 객체 이름만 밝히면 된다. 기본 타입의 int i; 선언문과 형식이 동일하다. ②번 형식은 디폴트 생성자를 명시적으로 호출하는 구문인데 효과는 동일하다. ③, ④번은 객체를 동적으로 생성할 때 new연산자와 함께 사용하는 방법인데 ③번이 더 일반적이다.

그러나 ⑤번 형식은 허용되지 않는다. 왜냐하면 이 선언문은 Position 객체를 리턴하고 인수를 가지지 않는 Here 함수의 원형을 선언하는 것이지 객체 선언문이 아니기 때문이다. 생성자로 전달할 인수가 없으면 아예 괄호도 없어야 한다. 일반 함수는 인수가 없을 때 빈 괄호를 써 함수임을 분명히 표시하지만 객체 선언문의 경우는 반대로 생성자의 인수가 없을 때 괄호를 생략해 함수가 아님을 분명히 해야 한다. 잘 이해가 안되고 순간적으로 헷갈린다면 정수형으로 바꿔 생각해 보자.

 

int func;        // 이건 변수

int func();     // 요건 함수

 

만약 클래스가 생성자를 전혀 정의하지 않으면 어떻게 될까? 이 경우 컴파일러가 자동으로 디폴트 디폴트 생성자(그러니까 컴파일러가 기본적으로 정의하는 디폴트 생성자)를 만든다. 컴파일러가 만들어주는 디폴트 생성자는 아무 것도 하지 않는 빈 함수이다. 이 때 객체의 초기화 방식은 일반 변수와 같은 규칙이 적용되는데 전역이나 정적 객체라면 모든 멤버가 0으로 초기화되고 지역 객체라면 초기화되지 않는 쓰레기값을 가진다.

생성자가 없을 경우 컴파일러가 디폴트 생성자를 만들기 때문에 생성자를 전혀 정의하지 않아도 객체를 선언할 수 있는 것이다. 위 예제에서 Position() 디폴트 생성자를 삭제하면 컴파일러가 내부적으로 다음과 같은 디폴트 생성자를 만들 것이다.

 

Position()

{

}

 

비록 아무 것도 하지는 않지만 생성자가 있으므로 Position Here; 선언문으로 Here 객체를 선언할 수 있다. 그러나 이 객체는 쓰레기값을 가지고 있기 때문에 OutPosition이 어떤 동작을 할 것인지는 예측할 수 없다. 일반적으로 예측할 수 없는 동작은 항상 말썽의 소지가 되며 이런 잠재적인 말썽의 소지를 없애기 위해 디폴트 생성자를 직접 정의하고 모든 멤버의 쓰레기를 치우는 것이다.

컴파일러가 디폴트 생성자를 만드는 경우는 클래스가 생성자를 전혀 정의하지 않을 때 뿐이다. 다른 생성자가 하나라도 정의되어 있으면 컴파일러는 디폴트 생성자를 만들지 않는다. 다음 코드를 보자.

 

class Position

{

public:

     int x;

     int y;

     char ch;

 

     Position(int ax) { x=ax; }

     void OutPosition() { ... }

};

 

정수 하나를 인수로 취하는 생성자가 정의되어 있으므로 이 클래스는 디폴트 생성자를 가지지 않는다. 이 경우 Position Here; 선언문은 적절한 생성자를 찾을 수 없으므로 에러로 처리될 것이다. 별도의 생성자를 제공했다는 것은 클래스를 만든 사람이 이 객체는 이런 식으로 초기화해야 한다는 것을 분명히 명시한 것이므로 컴파일러는 이 규칙을 어긴 코드에 대해 사정없이 에러로 처리한다. 이 객체는 개발자의 의도에 따라 반드시 Position Here(12); 형식으로 생성해야 한다.

만약 Position Here; 형태로 꼭 객체를 선언하고 싶다면 Position(int) 생성자를 없애 컴파일러가 디폴트 생성자를 만들도록 내 버려 두든가 아니면 Position() 디폴트 생성자를 오버로딩해야한다. 생성자가 인수를 가지고 있더라도 디폴트 인수 기능에 의해 디폴트 생성자가 되는 경우도 있다. 다음과 같은 원형을 가지는 생성자는 인수없이도 호출할 수 있으므로 디폴트 생성자를 겸한다.

 

Position(int ax=0, int ay=0, char ach=' ')

 

디폴트 생성자가 없는 클래스는 객체 배열을 선언할 수 없다. 왜 그런지 다음 예제로 이유를 알아보자.

 

  : NoDefCon

#include <Turboc.h>

 

class Position

{

public:

     int x;

     int y;

     char ch;

 

     Position(int ax, int ay, char ach) {

          x=ax;

          y=ay;

          ch=ach;

     }

     void OutPosition() {

          gotoxy(x, y);

          putch(ch);

     }

};

 

void main()

{

     Position There[3];

}

 

이 예제의 Position 클래스는 디폴트 생성자를 정의하지 않으며 세 개의 인수를 취하는 생성자만 정의되어 있다. 개발자가 별도의 생성자를 정의했으므로 컴파일러는 디폴트 생성자를 만들지 않는다. 따라서 Position형의 객체를 만들려면 Position A(1,2,'A'); 식으로 생성자에게 세 개의 인수를 전달해야 한다. 그렇다면 main의 Position There[3]; 선언문은 어떻게 처리될까?

Position형의 객체 3개를 배열로 생성하되 이때 각 객체의 생성자가 호출될 것이다. 그러나 선언문에 인수가 없기 때문에 호출할만한 생성자를 찾을 수 없으며 에러로 처리된다. Position There[3]; 선언문이 처리되려면 인수를 취하지 않는 디폴트 생성자(컴파일러가 만든 것이든 개발자가 직접 정의한 것이든)가 반드시 있어야 하는 것이다. 다음과 같은 선언문이 가능하리라 생각해 볼 수도 있다.

 

Position There[3]={{1,2,'x'},{3,4,'y'},{5,6,'z'}};

 

구조체 배열처럼 ={ } 다음에 각 배열 요소의 초기값을 나열하는 형식이다. {1,2,'x'} 초기식에 있는 값을 클래스 선언문에 나타나는 멤버의 순서대로 대입하면 될 것처럼 보인다. 객체 배열을 초기화할 때도 이런 문법이 지원된다면 좋겠지만 이 문장은 에러로 처리된다. 왜 컴파일러가 객체 배열에 대한 이런 편리한 초기식을 지원하지 못하는지 생각해 보자.

객체는 단순히 정보의 집합인 구조체보다는 훨씬 더 복잡하기 때문에 단순한 대입만으로는 초기화할 수 없다. 생성 단계에서 둘 이상의 입력값을 계산한 결과가 초기값이 될 수도 있고 Person 예제처럼 인수의 길이만큼 메모리를 동적으로 할당해야 하는 경우도 있다. 또한 멤버가 프라이비트 영역에 있을 경우 외부 선언문에서 함부로 멤버값을 변경하는 것도 허락되지 않는다. 이런 능동적인 동작을 하려면 결국 객체 초기화를 위해 생성자가 호출되어야 하는 것이다.

그렇다면 초기식의 값을 그대로 생성자의 인수로 전달하면 되지 않을까? 초기식에 {1,2,'x'}라고 되어 있으니 Position(1,2,'x') 생성자를 호출하면 일단 될 것처럼 보이지만 이것도 불가능하다. 왜냐하면 생성자가 반드시 모든 멤버를 선언된 순서대로 다 받아들여야 한다는 제약이 없기 때문이다. Position(char ach, int ax, int ay) 요런 식으로 생성자가 정의되어 있다면 컴파일러가 초기식의 값과 생성자 인수와의 대응관계를 잘못 판단하게 될 것이고 객체는 제대로 초기화되지 않는다.

그래서 컴파일러는 애매한 초기식으로부터 대충 비슷해 보이는 생성자를 호출하는 쓸데없는 서비스를 하기보다는 차라리 에러로 처리하는 것이 더 깔끔하다고 생각하는 것이다. 만약 객체의 배열을 선언하면서 각 객체를 꼭 초기화하려면 다음과 같이 ={ }괄호안에서 생성자를 일일이 호출해야 한다.

 

void main()

{

     int i;

     Position There[3]={Position(1,2,'x'),Position(3,4,'y'),Position(5,6,'z')};   

     for (i=0;i<3;i++) {

          There[i].OutPosition();

     }

}

 

이 선언문은 초기식에서 명시적으로 생성자를 호출했고 생성자로 전달되는 인수의 순서를 컴파일러가 분명하게 알 수 있으므로 문법적으로 문제도 없고 애매하지도 않다. 객체 배열을 선언하면서 초기화할 때는 이 방법이 정석이며 초기식없이 선언만 하려면 반드시 디폴트 생성자가 정의되어 있어야 한다.

생성자가 없을 때 컴파일러가 디폴트를 만드는 것처럼 파괴자의 경우도 디폴트가 있다. 컴파일러가 만드는 디폴트 파괴자도 생성자의 경우와 마찬가지로 아무 일도 하지 않는 빈 함수이다. 그래서 뒷정리를 할 필요가 없는 클래스라면 디폴트 파괴자를 그냥 사용하는 것도 가능하다. 즉, 파괴자가 없어도 된다는 얘기인데 사실 파괴자는 필요없는 경우가 훨씬 더 많다. 생성자가 특별한 처리를 하지 않고 단순히 멤버 변수에 값만 대입한다면 뒷정리를 할 필요가 없다. Position은 파괴자가 전혀 불필요한 클래스이다.

 

'JAVA' 카테고리의 다른 글

펌) immutable 이란  (0) 2013.10.15
grid I U D 포멧 JAVA 셋팅 [티폼 기준]  (0) 2013.06.27
Swing - windowBuilder 설치 하기.  (0) 2012.06.13
패키지 컴파일러  (0) 2012.05.15
자바로 컴퓨터 종료  (0) 2012.05.15
Posted by 사라링
BLOG main image
.. by 사라링

카테고리

사라링님의 노트 (301)
JSP (31)
J-Query (41)
JAVA (24)
VM-WARE (0)
디자인패턴 (1)
스크랩 (0)
스트러츠 (3)
안드로이드 (11)
오라클 (45)
우분투-오라클 (1)
이클립스메뉴얼 (6)
스프링3.0 (23)
자바스크립트 (10)
HTML5.0 (17)
정보처리기사 (1)
기타(컴퓨터 관련) (1)
문제점 해결 (3)
프로젝트 (2)
AJAX (4)
하이버네이트 (3)
트러스트폼 (11)
Jeus (2)
재무관리(회계) (5)
정규식 (5)
아이바티스 (8)
취미 (2)
소프트웨어 보안 관련모음 (0)
정보보안기사 (6)
C언어 베이직 및 프로그램 (3)
보안 관련 용어 정리 (2)
넥사크로 (6)
Total :
Today : Yesterday :