멤버 생성 리스트에 대해서 알아보면, 크게 Class의 컨스트럭터와 디스트럭터가 있다.
1. Constructor
: Object가 생성될 때 불러지는 함수.
클래스 이름과 같은 함수를 만들면 완성됨.
2. Destructor
: Object가 없어질 때 불러지는 함수
클래스 이름 앞에 ~가 붙은 함수를 만들면 완성됨.
ex)
Compiler Explorer로 어셈블리 코드를 확인해 보면,
메인 함수 안에서 다른 함수를 부르는 call 명령어를 확인해 보면,
처음에 고양이 컨스트럭터 Cat::Cat(),
다음으로 Cat::speak(),
마지막으로 고양이 디스트럭터 Cat::~Cat() 가 순서대로 불려지는 걸 확인할 수 있다.
즉 컨스트럭터와 디스트럭터는 클래스 안에서 마법처럼 동작하는 것이 아니라,
하나하나가 명확히 스택프레임을 가지고 있는 함수라고 볼 수 있다.
*주의사항*
: 가끔 이런 컨스트럭터와 디스트럭터의 특성을 이용해서,
멤버 object를 포인터로 만들어 주고,
Constructor에서 힙 공간에 object를 만들고 디스트럭터에서 delete 해주는 코드를 짜는 경우가 있는데
이론적으로 맞지만 바람직한 코드가 아니다.
힙에 오브젝트를 생성하고 싶으면 스마트포인터를 사용하는 것을 추천한다.
또한 오브젝트가 너무 크지 않다면 그냥 멤버 오브젝트를 만드는 것도 추천한다.
예제: Constructor로 멤버 변수 초기화하기
나이 1살을 생성자에 넣으면 처음 만들어 지는 순간 object들의 나이는 1살이 된다.
생성자에서는 매개변수를 받을 수도 있다. Cat(int age) 이런 식으로 쓰고 컨스트럭터를 만들게 되면 kitty는 3살, nabi는 2살
이런 방식으로 오브젝트 생성 시에 멤버 variable을 초기화 해 줄 수 있다.
멤버 초기화 리스트 Member Initializer List
이번에는 동물원 클래스를 하나 더 만들어 보자.
동물원 클래스에 생성자를 만들어 주고, 동물원 안에는 Cat 한마리가 mKitty라는 이름으로 하나 있다고 하자.
그리고 main 함수에서 cppZoo를 하나 만들어 보자.
여기서 만약 kitty의 나이를 argument로 바꾸고 싶다고 하면
생성자에서 int kittyAge 매개변수를 넣어주고
mKitty = Cat(kittyAge); 이런 방식으로 생성자를 만들 수 있을 것이다.
처음에 cppZoo(5) 를 넘겨주게 되면 아무런 문제 없이 컴파일 되는 걸 확인할 수 있는데,
문제가 없어 보이지만,
사실은 이 파트에서 고양이가 한 마리 만들어지고 사라진다!
그림과 함께 살펴보면,
1) cppZoo가 만들어 지며 스택 위에 zoo 오브젝트가 만들어 진다.
2) 이 내부에 mKitty가 존재한다. mKitty는 처음 만들어 질 때 생성자로 인해 mAge = 1 이 들어있다.
3) 그 이후에 동물원 컨스트럭터가 불러지며 5의 argument가 넘어오게 된다.
따라서 이 파트에서 스택 프레임이 하나 올라오고 그 안에서 임시로 고양이가 만들어 지고 그 나이는 5살이 된다.
tmpCat = 5 (임시란 의미)
4) 멤버 오브젝트 mkitty에 tmpkitty의 정보가 할당되며 복사된다. (1->5)
5) 마지막으로 컨스트럭터 함수가 끝나며 stackframe이 삭제되고 kitty object가 5살을 가지게 되는 것이다.
: 즉 이 과정에서 Cat 하나가 생성되고 멤버object에 할당 해준 뒤에 사라진 것.
이 임시 고양이 object가 쓸데없이 만들어지고 없어졌다고 볼 수 있다.
이를 예방하기 위해 member init list를 사용할 수 있다.
간단히 생성자 뒤에 멤버 오브젝트를 써 주고 괄호를 열고 initialize 해주고 싶은 object를 넣어주면 된다.
위 그림처럼 멤버 init list를 사용해서 컨스트럭터를 만들게 되면
임시 object가 만들어 지지 않고 바로 cppZoo가 만들어 질 때 mKitty는 5살로 초기화 될 수 있는 것이다.
고양이 오브젝트에서도 Cat(int age):mAge(age) 이런 식으로 init list로 바꿀 수 있다.
하지만 그 변수가 object가 아닌 간단한 primitive type 일 때는 머신코드가 아무런 차이가 없다는 것 기억해 두길 바란다.
이 파트를 더 자세히 알고 싶다면 cppreference의 member init list로 검색해 확인할 수 있다.
여기에 설명되어 있는 내용에 더해 exception의 경우 virtual call의 경우, 초기화의 순서 정도를 더 확인할 수 있다.
https://en.cppreference.com/w/cpp/language/constructor
결론
: class constructor을 통해서 멤버 object 변수를 초기화 할 때 이런 방식의 initializer list를 사용하면 된다는 것!
다음 챕터에서는 컨스트럭터에 대해 더 자세히, copy, move 컨스트럭터에 대해 더 자세히 알아보고 assignment 할당에 대해서도 더 알아볼 것이다.
'모던C++ > OOP' 카테고리의 다른 글
5. function overloading, operator overloading (0) | 2022.08.07 |
---|---|
4. 복사, 이동 생성자 - Copy/move constructor (1) | 2022.08.06 |
2. Static 스태틱 멤버 (0) | 2022.08.04 |
1. OOP 란? - 객체 지향 프로그래밍 소개 (0) | 2022.07.24 |