C++ Constructors and destructors.
1.Introduction.
보통 어떠한 객체의 내부적 성질, 상태 등에 관련된 변수들은 모두 private 범주에 두고, 그 객체가 외부에 하는 행동들은 함수로써 구현하여 public 에 두면 된다고 한다.
new 와 malloc 모두 동적으로 할당하지만 '무언가' 다르다고 했는데, new 의 경우 객체를 동적으로 생성하면서와 동시에 자동으로 생성자도 호출해준다.
2.C++ destructors
명확히 delete 를 지정하지 않는 한 자동으로 delete 가 되는 경우는 없다. 즉 동적으로 할당했던 X는 영원히 메모리 공간 속에서 둥둥 떠다닌다는 말.
사실 몇 바이트 정도 밖에 되지 않을 것이지만 X들이 쌓이고 쌓이게 되면 메모리 누수 (Memory Leak) 이라는 문제점이 발생하게 된다.
그렇다면, 만일 main 함수 끝에서 X가 delete 될 때, 즉 우리가 생성했던 객체가 소멸 될 때 자동으로 호출되는 함수 - 마치 객체가 생성될 때 자동으로 호출 되던 생성자 처럼 소멸 될 때 자동으로 호출되는 함수가 있다면 엄청 편리할 것이다.
C++ 에서는 이 기능을 지원하고 있다. 바로 '소멸자(Destructor)' .
생성자가 클래스 이름과 똑같이 생겼다면 소멸자는 그 앞에 ~ 만 붙여주시면 된다.
EX)"~(클래스의 이름)''
만약 X클래스의 소멸자가 있다고 가정하면, ~Marine(); 생성자와 한 가지 다른 점은, 소멸자는 인자를 아무것도 가지지 않는다는 것이다.
소멸하는 객체에 인자를 넘겨서 딱히 의미는 없다. 즉 소멸자는 오버로딩도 되지 않는다.
X가 NULL 이 아닐 경우에 (즉 동적으로 할당이 되었을 경우에) 만 delete 로 X을 삭제한다.
참고로 X자체가 char 의 배열로 동적할당 하기 때문에 delete 역시 delete [] X, 즉 [] 를 꼭 써주어야만 한다.
객체가 소멸될 때 소멸자가 호출된다고 말했는데, 즉 소멸자 호출 메세지가 뜬다..
즉 '객체가 파괴될 때 호출되는 소멸자' 를 확실하게 확인 가능하다.
강조하지만, 소멸자의 역할은 중요하다. 객체가 다른 부분에 영향을 끼치지 않도록 소멸되는 일은 매우 중요한 일이다.
소멸자가 하는 가장 흔한 역할은 객체가 동적으로 할당받은 메모리를 해제하는 일이라고 볼 수 있다. 또한 쓰레드 사이에서 lock 된 것을 푸는 역할이라던지 등의 역할을 수행하게 된다.
참고로 따로 생성자를 정의하지 않더라도 디폴트 생성자가 있던 것처럼, 소멸자도 디폴트 소멸자(Default Destructor) 가 있다.
물론, 아무런 역할은 하지 않기에 만일 소멸자가 필요 없는 클래스라면 굳이 소멸자를 따로 써줄 필요는 없다.
3.C++ copy constructor
동일한 사과를 만들어 내는 방법은 각각의 사과를 각각 생성자로 생성 할 수도 있지만, 1 개만 생성해 놓고, 그 한 개를 가지고 나머지 사과들은 '복사 생성' 할 수도 있다.
복사 생성자 (copy constructor) 정의부터 살펴본다.
Apple(const Apple & fruit); <== 복사 생성자의 표준적인 정의다.
즉, 복사 생성자는 어떤 클래스 Q 가 있다면,Q (const Q & i);라고 정의된다.
즉, 다른 Q 의 객체 i를 상수 레퍼런스로 받는 다는 뜻이다. 여기서 i가 const 이기 때문에 복사 생성자 내부에서 i의 데이터를 변경할 수 없고,
무조건 새롭게 초기화 되는 인스턴스 변수들에게 '복사' 만 할 수 있게 된다.
왜냐하면,const 레퍼런스로 인자를 받았기 때문이다.
포인터에서 const 의 용법이랑 하는 동작이 동일하다..
한 가지 중요한 점은 함수 내부에서 받은 인자의 값을 변화시키는 일이 없다면 꼭 const 를 붙이는 것이 낫다.
복사 생성자의 경우도, 인자로 받은 fruit의 값을 변경할 일이 없기 때문에 처음부터 const 인자로 받은 것이다.
이런 조치를 취하면 후에 발생 할 수 있는 실수를 효과적으로 막을 수 있다.
Ex) fruit.coord_x = coord_x.
인자로 받는 변수의 내용을 함수 내부에서 바꾸지 않는다면 앞에 const 를 붙여 주는 것이 좋다.
복사 생성자를 실제로 어떻게 이용하는지 살펴보도록 한다.
Ex)
Apple fruit1(3, 3);
Apple fruit2 (fruit1);
한 가지 더 보자면, Apple fruit3 = fruit2;================Q와 Apple fruit3; fruit3 = fruit2; ====================W는 다른 문장.
왜냐하면 Q는 말 그대로 복사 생성자가 1 번 호출되는 것이고, W은 그냥 생성자가 1 번 호출되고, fruit3 = fruit2; 라는 명령이 실행되는 것이다. 다시 말하자면, 복사 생성자는 '생성' 시에 호출된다는 것만 알면 된다. 사실 디폴트 생성자와 디폴트 소멸자 처럼, C++ 컴파일러는 이미 디폴트 복사 생성자(Default copy constructor) 를 지원해 주고 있다. 복사 생성자를 한 번 지운 다음 실행해도, 이전과 동일한 결과가 나타남을 알 수 있다. 디폴트 복사 생성자의 경우 기존의 디폴트 생성자와 소멸자가 하는 일이 아무 것도 없었던 것과는 달리 실제로 '복사' 를 해준다. 간단한 클래스의 경우 귀찮게 복사생성자를 써주지 않고도 디폴트 복사 생성자만 이용해서 복사 생성을 쉽게 처리할 수 있다.
Apple::Apple(const Apple & fruit)
{
price = fruit.price;
origin = fruit.origin;
coord_Q = fruit.coord_Q;
coord_W = fruit.coord_W;
color = fruit.color;
name = fruit.name;
}
그 다음 복사 생성자를 호출한 뒤에 fruit1 과 fruit2 상태를 살펴보자.
당연히 price, origin, ... 그리고 name 까지 모두 같은 값을 갖게 된다. 여기서 name 이 같은 값 - 즉 두 개의 포인터가 같은 값을 가진 다는 것은 Apple::~Apple()
{
if(name)
delete [] name;
}
fruit2 에서 일단 name 은 NULL 이 아니므로 delete [] name 이 수행되고, 이미 해제된 메모리에 접근해서 다시 해제하려고 하였기 때문에.'#Programming Language > C++' 카테고리의 다른 글
C++ About const and static. (0) | 2018.04.02 |
---|---|
C++ Template. (0) | 2018.04.02 |
C++ Creating excel part 2. (0) | 2018.04.02 |
C++ Creating excel part 1. (0) | 2018.04.02 |
C++ I / O in C++ (0) | 2018.04.01 |