본문 바로가기
언어/C++

[C++ 기본 공부정리] 14-4. OOP - 생성자(constructor)

by 민-Zero 2019. 12. 30.

공부 내용을 정리하는 목적 이므로 참고용으로만 읽어 주시기 바랍니다.

틀린 부분에 대한 지적은 감사합니다.

1. 생성자(constructor)

클래스를 정의한 후 클래스 객체를 생성하게 되면 메모리에 할당된다. 이때 클래스의 멤버 변수는 초기화되지 않은 상태이므로 사용할 수 없다. 일일이 멤버 변수에 접근하여 초기화 후 사용해도 되지만 만약 private처럼 외부 접근이 불가능한 상태라면 불가능하고 public설정이 된 멤버 함수를 이용하여 초기화해야 할 것이다. 이럴 경우에는 객체를 사용하기 전에 무조건 해당 함수를 실행하고 객체를 사용해야 한다. 따라서 C++에서는 객체의 생성과 동시에 멤버 변수를 초기화해주는 멤버 함수인 생성자(constructor)를 제공한다.

생성자는 C++에서 제공하는 멤버 함수 이므로 생성 방법이 존재한다.

 

문법)

클래스명(매개변수1, 매개변수2, ..);

 

생성자는 작성한 클래스명으로 선언할 경우 생성자가 된다. 멤버 변수의 초기화가 목적인 값을 반환하지 않는 특징을 가진 함수 이므로 반환 타입은 작성하지 않아도 된다. 또한 외부에서 클래스를 생성함과 동시에 동작해야 하므로 public영역에 생성해야 한다.

이렇게 클래스명과 동일하게 작성하면 이 함수는 생성자로 역할을 하며 객체 생성과 동시에 초기화를 진행하게 된다.  만약 생성자를 단 하나도 정의하지 않았다면 컴파일러가 자동으로 텅 빈 생성자를 호출한다. 예를 들면 Test() {}처럼 아무것도 존재하지 않는 생성자가 호출된다.

 

생성자의 특징을 정리하면 다음과 같다.

① 생성자는 멤버 변수의 초기화를 목적으로 가지는 함수 이므로 인수를 통해 값을 전달받을 수 있다.

② 값을 반환하지 않지만 void로 따로 선언하지 않는다.

③ 생성자는 3가지 방법으로 생성할 수 있다. 객체 초기화 방법이 여러 개일 경우 오버로딩을 통해 여러 개 생성자를 만드는 것도 가능하다. 

 

▶생성자 3가지 타입: 기본 생성자, 디폴트 생성자, 복사 생성자

 

2. 기본 생성자

가장 기본적인 생성자는 매개변수를 통해 객체가 생성될 때 전달받은 인자 값으로 멤버 변수를 초기화한다.

 

다음과 같이 멤버 변수에 타입에 맞는 매개변수를 통해 인자 값을 넘겨주어 초기화해준다. 이렇게 생성한 생성자를 통해 객체를 생성하면서 초기화하는 방법으로 암시적 호출과 명시적 호출이 존재한다.

 

암시적 호출)

생성하는 객체 옆에 ()를 통해 바로 멤버 변수를 초기화하는 방법으로 넘겨준 인자 값을 통해 멤버 변수가 초기화되었다. 생성자가 언제 호출되는지 확인하기 위해 "생성자 호출"이라는 문자열을 출력하게 코드를 추가하고 결과를 확인해보니 printNum() 함수가 호출되기 전인 객체 생성과정에서 바로 생성자가 호출되는 것을 확인할 수 있다. 

 

명시적 호출)

명시적 호출을 통해 생성자를 사용했다. 객체 생성을 위한 작성법만 다를 뿐 암시적 생성과 동일한 기능을 수행한다.

 

3. 디폴트 생성자(default constructor)

디폴트 생성자는 객체를 생성할 때 아무런 인자를 넘겨주지 않을 때 멤버 변수를 초기화하는 생성자이다. 앞에서 말한 아무런 생성자를 생성하지 않을 경우 컴파일러가 자동으로 생성하는 텅 빈 생성자 또한 디폴트 생성자이다. 매개변수를 가지지 않으므로 대부분 0이나 Null, 빈 문자 열등을 이용하여 초기화한다.

생성자를 만들지 않으면 자동으로 컴파일러는 위의 예시와 같은 생성자를 생성하여 초기화를 진행하게 된다.

 

디폴트 생성자를 이용하는 방법은 다음과 같다.

기본 생성자의 매개변수에 디폴트 값을 주어 디폴트 생성자를 만들 수 있다.

디폴트 생성자의 경우 인수로 아무런 값을 넘기지 않을 때 실행되므로 객체 생성 과정에서 ()를 통해 값을 넘기지 않고 Test test;처럼 암시적으로 객체 생성만 하면 된다. 명시적으로 생성하고 싶다면 Test test = Test();로 수행하면 된다.

 

또는 디폴트 생성자 내부에서 멤버 변수의 값을 정해놓고 수행하면 된다.

 

4. 복사 생성자(copy constructor)

복사 생성자를 이해하기 위해 먼저 얕은 복사와 깊은 복사에 대한 차이를 알아야 한다. 일반 변수에서의 값의 복사의 경우에는 크게 문제 되지 않았지만 객체를 복사하는 경우 기존 객체와 같은 값을 가지는 새로운 객체를 만들어야 하는데 객체는 다양한 멤버를 가지고 있고 그 멤버가 값일 수도 참조 형식일 수도 있기 때문에 깊은 복사와 얕은 복사가 나뉜다.

 

-얕은 복사(shallow copy) : 얕은 복사는 객체를 복사할 때 기존 객체의 멤버가 할당한 메모리 주소만 복사하여 동일한 값을 가지도록 하는 것을 뜻한다.

-깊은 복사(deep copy) : 깊은 복사는 객체가 가진 멤버의 값과 형식 자체를 복사하여 객체 자체가 복사되는 것을 뜻한다.

 

복사 생성자는 동일한 클래스 타입의 다른 객체에 대한 참조(reference)를 인수로 받아서 생성하는 객체를 초기화하도록 하는 깊은 복사를 통한 복사이다. 따라서 복사 대상인 원본 객체와 같으면서 서로 독립적인 다른 객체가 되도록 해준다. 만약 깊은 복사를 통하지 않는다면 두 객체 중 하나의 값이 바뀐다면 나머지 값도 같이 변경된다.

복사 생성자의 암시적 호출은 ()에 복사하고자 하는 객체를 입력하면 된다.

명시적으로 호출하고자 하면 대입 연산자를 통해 객체를 전달하면 된다.

 

5. 멤버 이니셜라이저(member initializer)

멤버 변수를 초기화하는 방법 중 하나로 지금 까지 설명한 생성자를 통한 멤버 변수 초기화 방법보다 성능이 뛰어나다고 알려져 있다.  이유는 바이너리 코드 상에서 그냥 생성자를 사용해 초기화할 경우 

int num1;

num1 = 10;

과 같이 구성되어 초기화되지만 이니셜라이저를 사용할 경우

int num1 = 10;

로 초기화가 동시에 진행되어 좀 더 뛰어난 성능을 발휘할 수 있다. 또한 이와 같은 이유로 const 멤버의 경우 생성과 동시에 초기화되어야 하기 때문에 const 멤버 변수는 이니셜라이저를 통해 초기화를 해야 한다.

 

문법)

클래스명():멤버 변수1(값 또는 변수), 멤버 변수2(값 또는 변수),..{ 추가 초기화 }

 

생성자이므로 클래스명을 그대로 사용하고 콜론(:)을 통해 멤버 변수 목록을 사용한다. 멤버 변수 옆의 ()안의 값으로 각각 멤버 변수를 초기화하게 되며 멤버 변수의 구분은 쉼표(,)를 이용한다. 추가 초기화 부분은 필요시에만 작성하면 된다.

 

 

댓글