[디딤돌 C++] 16. 생성자, 소멸자

학생) 생성자는 형식 이름과 같고 소멸자는 ~형식이름

이번에는 개체를 생성할 때 수행하는 생성자와 소멸할 때 수행하는 소멸자를 알아볼게요.

 

생성자는 개체를 생성할 때 수행할 기능을 정의하는 특별한 메서드입니다. 생성자는 반환 형식을 개발자가 정할 수 없으며 메서드 이름을 형식 이름과 같게 정의합니다.

 

그리고 소멸자는 개체를 소멸할 때 수행할 기능을 정의하는 특별한 메서드예요. 소멸자도 반환 형식을 개발자가 정할 수 없으며 메서드 이름은 ~형식 이름이예요.

 

C++ 언어에세 생성자는 개체가 만들어지는 시점에 동작합니다. 예를 들어 Student stu; 처럼 클래스 형식 변수 선언문에 의해 stu 개체가 만들어질 때도 동작하고 Student *stu = new Student(); 처럼 new 연산자를 사용하여 동적으로 개체를 생성할 때도 동작합니다.

소멸자는 개체의 메모리를 해제할 때 동작합니다. Student stu; 처럼 선언한 개체는 선언한 지역이 끝날 때 메모리를 해제하기 전에 소멸자를 먼저 수행합니다. Student *stu = new Student(); 처럼 동적으로 생성한 개체는 delete stu; 처럼 delete 연산으로 메모리 해제를 요청하면 소멸자를 먼저 수행하고 메모리를 해제합니다.

▷ 실행 결과

 

  1. 디폴트 기본 생성자, 디폴트 소멸자

클래스를 정의할 때 생성자와 소멸자를 정의하지 않으면 컴파일 할 때 접근 지정이 public인 디폴트 기본 생성자와 소멸자를 만들어 줍니다.

 

디폴트 기본 생성자와 소멸자가 실질적으로 수행하는 작업은 없지만 형식 외부에서 개체를 생성하거나 소멸할 수 있게 가시성을 제공하는 역할을 합니다.

 

DemoA 클래스에 생성자와 소멸자를 정의하지 않고 개체를 동적 생성 및 소멸을 요청하는 것은 가능합니다. 개발자가 정의하지 않으면 컴파일러가 접근 지정이 public인 디폴트 기본 생성자와 소멸자를 만들어 주기 때문이죠.

하지만 DemoB 클래스에 생성자와 소멸자를 정의하고 접근 지정자를 private으로 설정하면 디폴트 기본 생성자와 소멸자는 만들어 주지 않습니다. 이 때는 개발자가 정의한 생성자와 소멸자의 접근 지정이 private 이므로 형식 외부에서 개체를 생성할 수 없습니다.

 

다음은 이를 테스트하는 예제 코드예요.

다음은 이를 컴파일했을 때 발생하는 오류 화면을 캡쳐한 것입니다.

error C2248: DemoB::DemoB private 멤버에 액세스할 수 없습니다.error C2248: DemoB::~DemoB private 멤버에 액세스할 수 없습니다.

 

  1. 생성자 중복 정의

C++에서는 개체를 생성할 때 수행할 생성자를 중복 정의할 수 있습니다. 물론 중복 정의 문법에 따라 입력 매개 변수 리스트가 달라야겠죠.

 

참고로 소멸자는 중복 정의할 수 없어요.

 

사용하는 코드에서는 생성할 때 전달하는 인자에 따라 적절한 생성자를 호출합니다.

 

다음은 생성자 중복 정의한 예제 코드입니다.

▷ 실행 결과

 

3. 복사 생성자

생성자 중에 입력 인자로 같은 형식의 개체를 인자로 받는 생성자를 복사 생성자라고 부릅니다. 개발자가 복사 생성자를 정의하지 않으면 컴파일러가 디폴트 복사 생성자를 만들어요.

 

디폴트 복사 생성자는 입력 인자로 받은 개체의 메모리를 생성하는 개체의 메모리에 그대로 복사합니다. 이와 같이 개체의 메모리만 복사하는 것을 얕은 복사라고 말합니다.

 

다음은 디폴트 복사 생성자에 동작을 확인하는 코드입니다.

▷ 실행 결과

 

그런데 형식 내부에서 동적으로 생성한 개체를 갖고 있을 때는 개발자가 복사 생성자를 정의해야 할 필요가 있습니다.

정수를 보관하는 동적 배열을 예로 들게요.

동적 배열 클래스의 클래스 다이어그램

동적 배열 개체를 생성할 때 보관할 원소의 최대 개수를 입력받아 저장소를 동적으로 생성하게 합시다.

 

개체 내부에서 저장소를 동적 생성하므로 소멸자를 정의하여 이를 소멸하는 부분을 구현해야겠죠.

 

그리고 순자 보관하는 PushBack 메서드를 제공합니다.

 

보관한 데이터 목록을 출력하는 메서드도 제공합시다.

 

이처럼 동적 배열 클래스를 만들었을 때 복사 생성하면 어떻게 동작하는지 알아볼게요.

얕은 복사로 복사 생성한 개체와 원본 개체는 같은 버퍼를 갖는 논리적 버그가 발생합니다.

 

복사 생성하면 단순히 dcarr1의 메모리를 dcarr2의 메모리에 복사하는 작업만 하기 때문에 내부 저장소를 별도로 생성하지 않고 같은 저장소를 가리킵니다.

 

그리고 dcarr1으로 9를 보관하면 저장소의 인덱스 1에 9를 보관하고 보관 개수인 usage를 1 증가하여 2로 변하겠죠. 그리고 dcarr2로 6을 보관하면 dcarr2의 usage멤버는 1인 상태이므로 저장소의 인덱스 1에 6을 보관하고 보관 개수인 usage를 1 증가하여 2로 변합니다.

 

이처럼 두 개의 배열 개체가 하나의 저장소를 사용하여 논리적 버그가 발생합니다. 이 외에도 소멸자에서 저장소를 해제하는 과정에서 한 쪽은 이미 해제한 저장소를 해제 요청하여 런타임 오류가 발생합니다.

다음은 앞에서 설명한 내용을 작성한 코드입니다.

 

만약 개체 내부에서 동적으로 생성한 다른 개체를 멤버로 갖고 있다면 개발자가 복사 생성자를 중복 정의하세요. 개체의 메모리를 복사하는 얕은 복사만 할 것이 아니라 내부에 개체도 복사하여 갖게 하세요. 이와 같은 복사를 깊은 복사라고 부릅니다.

 

다음은 앞의 코드를 깊은 복사를 수행하는 복사 생성자를 정의한 예제 코드입니다.

깊은 복사로 복사 생성한 개체는 원본 개체와 독립적인 개체입니다.


[C++ 무료 동영상 강의] 16. 생성자, 소멸자 (Part1)

[C++ 무료 동영상 강의] 16. 생성자, 소멸자 (Part2)

[C++ 무료 동영상 강의] 16. 생성자, 소멸자 (Part3)