생성자
생성자는 객체 생성 시 자동으로 호출되면서 객체를 초기화 해주는 함수이다.
Date 클래스의 day 객체를 만들면 생성자 Day(int year, int month, int day)가 호출된다. 따라서 Date의 객체를 생성할 때 생성자의 인자 year, month, day에 각각 2011, 3, 1을 전달하며 객체를 생성하게 되는 것이다.
Date day(2021, 7, 9); // 암시적 방법 (implicit)
Date day = Date(2021, 7, 9); // 명시적 방법 (explicit)
함수를 호출하듯이 사용하는 것이 암시적 방법, 명시적으로 생성자를 호출한다는 것을 보여주는 것이 명시적 방법인데 많은 경우 암시적 방법으로 축약해서 쓸 수 있으므로 이를 선호하는 편이다.
디폴트 생성자
Date day;
와 같이 객체를 초기화 할 때 생성자를 명시하지 않았을 때는 디폴트 생성자가 생성된다. 디폴트 생성자는 인자를 하나도 가지지 않는 생성자인데, 클래스에서 사용자가 어떠한 생성자도 명시적으로 정의하지 않았을 경우에 컴파일러가 자동으로 추가해준다.
※ 사용자가 다른 생성자를 추가한 순간 컴파일러는 자동으로 디폴트 생성자를 삽입하지 않는다는 것을 명심하기
직접 디폴트 생성자를 정의할 수도 있다. 한 가지 주의할 점은 인자가 있는 생성자에서 적용했던 것처럼 Date day3(); 이라 하면 객체를 디폴트 생성자를 이용해서 초기화하는 것이 아니라, 리턴값이 Date이고 인자가 없는 함수 day3을 정의한 것으로 인식한다.
class Test {
public:
Test() = default; // 디폴트 생성자를 정의해라
};
위처럼 생성자 선언 바로 뒤에 = default 를 붙여준다면, Test의 디폴트 생성자를 정의하라고 컴파일러에게 명시적으로 알려줄 수 있다.
생성자 오버로딩
생성자 역시 함수이기 때문에 함수의 오버로딩이 적용될 수 있다.
복사 생성자
복사 생성자를 이해하기 위해 먼저 얕은 복사와 깊은 복사의 차이를 알아야 한다. 일반 변수에서의 값의 복사의 경우에는 크게 문제도지 않았지만 객체를 복사하는 경우 기존 객체와 같은 값을 가지는 새로운 객체를 만들어야 하는데 객체는 다양한 멤버를 가지고 있고 그 멤버가 값일수도 참조 형식일수도 있기 때문에 깊은 복사와 얕은 복사로 나뉜다.
- 얕은 복사(shallow copy) : 객체를 복사할 때 기존 객체의 멤버가 할당한 메모리 주소만 복사하여 동일한 값을 가지도록 하는 것- 깊은 복사(deep copy) : 객체가 가진 멤버의 값과 형식 자체를 복사하여 객체 자체가 복사되는 것
복사 생성자는 동일한 클래스 타입의 다른 객체에 대한 참조(reference)를 인수로 받아서 생성하는 객체를 초기화하도록 하는 깊은 복사를 통한 복사이다. 따라서 복사 대상인 원본 객체와 같으면서 서로 독립적인 다른 객체가 되도록 해준다. 만약 깊은 복사를 하지 않는다면 두 객체 중 하나의 값이 바뀐다면 나머지 값도 변경된다.
멤버 이니셜라이저(member initializer)
멤버 변수를 초기화하는 방법 중 하나로 지금까지 설명한 생성자를 통한 멤버 변수 초기화 방법보다 성능이 뛰어나다고 알려져 있다. 그 이유는 바이너리 코드 상에서 그냥 생성자를 사용해 초기화할 경우
int num1;
num1 = 10;
과 같이 구성되어 초기화되지만 이니셜라이저를 사용할 경우
int num1 = 10;
으로 초기화가 진행되어 좀 더 뛰어난 성능을 발휘할 수 있다. 또한 이와 같은 이유로 const 멤버와 레퍼런스의 경우 생성과 동시에 초기화되어야 하기 때문에 이니셜라이저를 통해 초기화해야 한다.
클래스명() : 멤버 변수1(값 또는 변수), 멤버 변수2(값 또는 변수), .. {추가 초기화}
소멸자
생성자가 클래스 이름과 똑같다면 소멸자는 그 앞에 ~만 붙여주면 된다. 생성자와 다른 점은, 소멸자는 인자를 아무것도 가지지 않고 오버로딩도 되지 않는다.
가장 먼저 main 함수에서 a 객체를 생성하였으므로 a의 생성하였으므로 a의 생성자가 호출된다. 그리고 simple_function을 실행하게 되면, simple_function 안에서 또 b 객체를 생성하므로 b의 생성자가 호출된다. 하지만 b는 simple_function의 지역 객체이기 때문에 simple_function이 종료됨과 동시에 b 역시 소멸하게 된다. main 함수가 종료될 때 마찬가지로 main 함수의 지역 객체였던 a가 소멸되면서 a의 소멸자가 호출된다.
소멸자의 역할은 중요하다. 소멸자가 하는 가장 흔한 역할은 객체가 동적으로 할당받은 메모리를 해제하는 일이다. 그 외에도 쓰레드 사이에서 lock된 것을 푸는 역할 등을 수행하게 된다.
'Language > C++' 카테고리의 다른 글
[C++] OOP 캡슐화 - static, const 멤버 (0) | 2021.10.27 |
---|---|
[C++] OOP 캡슐화 - 프렌드(friend) (0) | 2021.10.25 |
[C++] OOP - this 포인터 (0) | 2021.10.24 |
[C++] OOP - 클래스(class) (0) | 2021.10.24 |
[C++] <random> 라이브러리 (난수 생성) (0) | 2021.10.03 |