안녕하세요.
오늘은 지난번 클래스에 대한 간단한 개념과
접근제어지시자에 이어
class내의 특별한 함수들에 대해서
이야기해보려 합니다.
바로 생성자와 소멸자인데요.
생성자와 소멸자는 그 생김새부터 역할,
호출 시기와 소멸 시기까지 조금은 특별한 함수입니다.
그리고 간단하게 this포인터까지 짚고 넘어갈 것이기 때문에
잘 봐주시기 바랍니다.
그럼 시작하겠습니다.
1. 생성자
생성자란 어떠한 클래스의 객체가 생성될 때 호출되는 함수를 말합니다. 즉 클래스의 인스턴스를 선언하게되면 그 즉시 자동으로 호출되는 함수입니다. 코드로 보겠지만 생성자와 소멸자는 return 값이 없기 때문에 다른 함수와 달리 이름 앞에 반환 자료형을 기술하지 않습니다.(void도 기술하지않음) 또 생성자는 클래스의 이름을 그대로 가져가고, 소멸자는 클래스 이름앞에 ~표시를 붙인 것으로 기술합니다. 아마 당분간은 이 생성자에 대해 얘기할 것이 많을 것입니다. 가장 많이 얘기 듣는 복사생성자 붙어, 매개변수가 하나뿐인 변환생성자, 이동생성자 등 생성자의 종류는 다양하고 대부분 여러분들이 정말 잘 알아야하는 개념입니다. 오늘은 생성자의 기본에 대해서 간략하게 알아보는 시간을 갖도록 하겠습니다. 코드를 봅시다.
생성자를 봅시다. 위에 보시면 클래스의 이름과 똑같은 CTest 라는 함수가 바로 생성자입니다. 위에 설명했던 대로 클래스의 생성자 이름은 그 클래스의 이름을 그대로 따라갑니다. 그런데 조금은 신기한 점이 있습니다. 함수 이름이 클래스를 그대로 따라간다는 것은 알겠는데, void를 비롯한 함수 이름 앞에 반환형식이 전혀 기술되어있지 않습니다. 왜냐하면 생성자는 그 어떠한 형식도 반환하지 않기 때문입니다.
void도 알고보면 어찌됐든간에 반환형식입니다. 그러나 생성자와 소멸자는 그 어떠한 것도 반환하지 않기때문에 어떤 리턴 형식도 기술하지 않는 것입니다. 그리고 생성자는 클래스가 생성될 때 자동으로 호출합니다. 메인 함수를 보면 아무것도 하지 않고 단지 CTest의 인스턴스를 생성했을 뿐인데 결과에 10이 출력되는 것을 볼 수 있습니다. 이는 CTest의 인스턴스 test를 선언할 때 기본적으로 CTest() 생성자 함수가 호출되어 nData의 값을 출력하기 때문입니다. 다시 한번 말하자면 생성자 함수는 클래스의 이름과 이름이 똑같고 반환형식이 아예 없으며 클래스의 객체가 생성될 때 자동으로 생성된다고 설명했습니다. 그러나 반환형식이 없지 파라미터도 받지 않을까요? 다음 코드를 봅시다.
다음 코드의 13번 라인을 봅시다. 위의 코드와 다르게 이번에는 CTest의 생성자가 매개변수 nParam을 받고 nData에 nParam의 값을 대입하는 것을 볼 수 있습니다. 그 후 21번 라인을 보면 CTest test(10) 으로 생성자에 매개변수 10을 넘기는 코드를 볼 수 있습니다. 그로 인해 콘솔창에 결과는 10이 정상적으로 찍히는 것을 볼 수 있습니다. 즉 생성자에는 매개변수를 넘겨서 클래스의 멤버 변수에 값을 넣을 수 있습니다.
생성자가 무엇인지 조금은 감이 잡히셨나요?? 우리가 한 번 생각해볼 것은 클래스의 객체가 생성될 때 생성자가 자동으로 호출된다는 것입니다. 이는 생성자에는 그렇다면 어떤 코드를 넣어야 하는지 방향을 제시해줍니다. 느낌이 오셨나요? 보통 생성자 함수는 시작단계이므로 멤버변수의 값을 초기화해주는 역할을 주로 합니다. 다른 기능들도 할 수 있지만 일단 생성자코드는 기본적으로 멤버변수 값을 초기화하게 만든다고 알고계시면 좋을 것 같습니다. 그렇다면 생성자를 이용하여 멤버변수를 초기화하는 방법들을 알아보겠습니다.
2. 멤버변수 초기화 방법
기본적으로 생성자에 매개변수로 초깃값을 전달해서 생성자 함수 내부에서 멤버변수에 그 값을 대입하는 방식입니다. 매개변수를 받지않고 생성자에 nData = 10;처럼 하는 코드는 다들 이해하실테니 넘겼습니다. 그럼 위와 같은 코드말고 무슨 방법이 있을까요? 바로 초기화 목록이라는 기능을 사용할 수 있습니다. 코드를 보겠습니다.
위의 생성자를 보면 매개변수자리 옆에 : 를 작성한 후 멤버변수(파라미터) 형식을 볼 수 있습니다. 이와 같은 방식이 생성자 초기화 목록입니다. 위와 같은 방식을 꼭 써야할 때는 참조자를 멤버변수로 선언할 때 입니다. 참조자는 선언과 동시에 초기화 해야 합니다. 즉 함수내부에서 멤버 참조자 변수에 값을 대입하려하면 이미 생성된 참조자가 초기화가 안되어있단 이유로 에러가 발생합니다. 그러나 초기화 목록을 사용하면 참조자 변수 초기화를 정상적으로 완료할 수 있습니다. 다음 멤버변수 초기화 방법을 봅시다.
제 기억으로는 아마 정확한 버전은 기억안나지만 C++ 어느 버전 이상부터 위와 같은 초기화 기능을 제공하는 것으로 알고 있습니다. 위처럼 하면 생성자 함수에서 귀찮게 기술할 필요없이 바로 초깃값을 넣을 수 있습니다. 단점이라고 하면 매개변수로써 초기화를 하고 싶을 때는 위와 같은 방법으로는 불가능하고 꼭 생성자 함수를 이용해야한다는 것입니다.
위와 같이 여러 멤버변수 초기화 방법이 있는데 여러분의 코드에 맞게 사용하시면 될 것 같습니다. 그럼 생성자를 알아봤으니 이제 소멸자에 대해서 알아보겠습니다.
3. 소멸자
15번 라인을 봅시다. 이것이 바로 소멸자 입니다. 소멸자는 클래스의 이름 앞에 물결표시를 하나 더 붙이면 됩니다. 소멸자는 객체가 소멸할 때 자동으로 호출합니다. 여기서 생성자와 소멸자의 또 다른 차이는 소멸자는 파라미터 또한 줄 수 없습니다. 그 이유는 바로 아시는 분도 계실텐데 생성자는 객체를 생성하는 쪽이 언제 생성하냐를 정할 수 있어 매개변수를 넘길 수 있지만 소멸자는 호출위치를 사용자가 정하는 것이 아니라 객체가 소멸할 때 자동 호출 되므로 매개변수를 넘길 타이밍이 없습니다. 일단 생성자는 대개 멤버변수를 초기화 한다고 배웠습니다. 그럼 소멸자는 보통 언제 쓰이냐면 클래스 내에서 어떤 변수를 동적할당 했다고 가정하면 그 메모리를 해제하는 용으로 많이 쓰입니다. 즉 free()함수나 delete연산자를 사용할 때 소멸자에 기술합니다. 그 이유는 클래스가 소멸할 때 자동으로 호출된다는 것은 동적할당한 메모리를 제거하기 좋은 타이밍을 갖고 있다는 말이기 때문입니다. 소멸자는 딱히 어려운 것이 없습니다. 그렇다면 우리가 헷갈릴만한걸 한번 짚고 넘어가겠습니다. 생성자와 소멸자의 호출 타이밍에 대해서 알아보겠습니다.
두 객체를 생성한 생성하여 각각의 생성자와 소멸자 호출 시기를 알아보겠습니다. 결과창을 보면 test1, test2가 선언됨과 동시에 생성자가 호출된 것을 확인할 수 있습니다. 그 후 메인함수에 적어놓은 코드가 실행되었고 눈여겨 볼 것은 소멸자 호출 순서 입니다. 결과를 보면 2번 객체의 소멸자가 먼저 호출된 것을 볼 수 있습니다. 다시 말해 소멸자 호출 순서는 생성자 호출 순서, 즉 객체 선언 순서와 반대로 호출됩니다. 그 이유는 stack의 특징 선입후출에 대해 알고 있으면 쉬울 것 같습니다. 이렇게 생성자와 소멸자의 호출 시기를 알아보았습니다.
4. this 포인터
이번 포스팅의 마지막으로 간단하게 this포인터에 대해서 알아보도록 하겠습니다. this라는 키워드는 객체 자기 자신을 가리키는 포인터 입니다. 우선 코드를 봅시다.
CTest의 생성자를 보면 멤버변수와 매개변수 이름 둘다 nData라는 것을 알 수 있습니다. 이런 것을 보고 모호하다고 표현하는데요
그렇다면 생성자 함수 내부에 nData를 출력한다면 매개변수 값인 1이 호출될까요 아니면 멤버면수 값인 10이 호출될까요??
정답은 1입니다. 블록스코프를 먼저 검사하기 때문에 매개변수의 우선순위가 높기 때문입니다. 그러나 this포인터를 사용하면 멤버변수와 매개변수의 이름이 같다고 하더라도 모호성을 없앨 수 있습니다. 코드를 봅시다.
위와 같이 하면 멤버변수 nData라는 것을 명확히 해줄 수 있습니다. this포인터는 자기자신을 가리키는 포인터 입니다. 포인터 이기 때문에 ->로 멤버에 접근할 수 있습니다. 즉 멤버변수라는 것을 명확히 해줄 수 있는 것이죠. 자기 자신을 가리키는 것이기 때문에 이 외에도 다양한 용도로 사용할 수 있습니다. 여러분들이 c++을 하다보면 자연스럽게 여러곳에서 this를 사용하는 것을 볼 수 있을 것입니다.
참고로 멤버변수와 그 멤버변수의 초깃값을 받는 파라미터의 이름이 같으면 14번 처럼 하게 되면 모호하지 않고 정상적으로 멤버변수 nData의 값을 초기화 할 수 있습니다.
이번 포스팅에서는 생성자와 소멸자, 그리고 this포인터에 대해서 다뤄봤습니다. 클래스를 사용하는 데 있어서 굉장히 중요한 내용이기 때문에 잘 숙지하시길 바랍니다. 그럼 다음에 또 오겠습니다.
참고자료
https://youtube.com/playlist?list=PLXvgR_grOs1DFOWF65X0Zqnd_264x41u-
'C++' 카테고리의 다른 글
C++ 복사생성자 : 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy) (0) | 2022.03.17 |
---|---|
C++ 두번째, 클래스(Class) 기본 개념과 접근 제어 지시자 (0) | 2022.02.23 |
C++ 첫번째, 간단하게 보는 C와의 차이점 (0) | 2022.02.12 |