Con(De)structor/생성(소멸)자
Copy-constructor/복사 생성자
C++의 Class는 접근제어 뿐 아니라 여러가지 기능을 지원한다.
생성자
생성자는 Class와 동일한 이름을 가지고 객체가 생성될 때 호출된다.
Class 첫번째 글에 Code 2-1를 살짝 수정해보자.
class Person { private: char name[10]; int pox, poy; public: Person(const char name[], int pox, int poy) { strcpy(this->name, name); this->pox = pox; this->poy = poy; } void ShowPostion() { std::cout << pox << " " << poy << std::endl; return; } void work() { pox++; poy++; } }; int main(){ Person man("man", 10, 10); man.ShowPostion(); man.work(); man.ShowPostion(); return 0; }
public아래 Person(…)이 생성자다.
기존의 코드에선 초기화를 해주는 함수를 따로 지정해 줫지만,
생성자를 선언한 후엔 man(“man”, 10, 10)으로 생성자를 호출해 초기화 해주는 것을 볼 수 있다.
물론 private 안에서도 생성자를 선언할 수 있다 하지만 private 안에 있는 생성자는 class내에서 밖에 접근하지 못한다.
++멤버 이니셜라이저 (Member initializer)
Class안의 함수를 호출할 때 Class로 선언된 변수를 초기화 할 수 있는 방법이다 아래 클래스를 보자.
class Trip { private: Person man; Person woman; int number; public: Trip(const char name1[], const char name2[], const int pox1, const int poy1, const int pox2, const int poy2) :man(name1, pox1, poy1), woman(name2, pox2, poy2), number(pox1) { // Code } };
Trip이란 class 안에서 선언된 Person class들을 초기화 할 때,
:man(name1, pox1, poy1), woman(name2, pox2, poy2)
이처럼 초기화 해줄 수 있다.
또한 number(pox1)
처럼 상수도 같은 방식으로 초기화 해줄 수 있다.
소멸자
소멸자는 생성자와 정 반대 이다.
class가 사라질 때 호출 되는 함수이다. 표현식은 ~ClassName(){}으로 표현한다.
class Person { private: char name[10]; int pox, poy; char * dump; public: Person(const char name[], int pox, int poy) { strcpy(this->name, name); this->pox = pox; this->poy = poy; this->dump = new char[pox]; } void ShowPostion() { std::cout << pox << " " << poy << std::endl; return; } void work() { pox++; poy++; } ~Person() { delete[]dump; std::cout << "Call Desconstructor" << std::endl; } }; int main(){ Person man("man", 10, 10); man.ShowPostion(); man.work(); man.ShowPostion(); return 0; }
~Person() { .. } 에서 생성자에서 생성된 dump 객체를 delete한다.
이렇게 깜빡하고 비우지 못한 메모리를 Class 사용이후에 자동으로 비울 수 있게 해준다.
복사 생성자
뭔가 했다 그리 어려운게 없다.
만약 Person man1("man1", 10, 10)
이렇게 생성한 객체를 Person man2=man1
이렇게 직접 복사해 넣는다고 해보자
그러면 자동적으로 생성된 복사 생성자가 Class의 각각의 변수를 잘 넣어주는 기본 복사 생성자를 만들어 넣어준다.
하지만 이를 직접 변경할 때는 생성자를 오버라이트하여 아래와 같이 설정해주면 된다.
Person(Person copy){ ... }
깊은 복사/ 얕은 복사..?
이러한 이야기가 나오게 된건 위에 자동으로 만들어진 복사 생성자를 사용할 때 문제가 발생하기 때문이다.
class Person { private: [Skip] char * dump; public: Person(const char name[], int pox, int poy) { [Skip] this->dump = new char[pox]; } void ShowPostion() { [Skip] } void work() { [Skip] } ~Person() { delete[]dump; std::cout << "Call Desconstructor" << std::endl; } }; int main(){ Person man1("man", 10, 10); Person man2 = man1; [Skip] return 0; }
위 소스코드를 실행하면 “Call Desconstructor”가 하나만 나오고 에러가 출력되는데,
이는 Person Class안에 dump 포인터가 가르키는 대상이 없어졋기 때문이다.
일반적으로 기본 복사 생성자는 *(포인터) 값을 복사하고, 포인터가 가르키는 *dump 값은 복사하지 않기 때문이다.
이미 man1의 소멸자가 delete[]dump를 했는데 복사된 man2에서 delete[]dump를 하니 에러가 발생하는 것이다.
이를 위해 깊은 복사. 즉 * 가 가르키는 대상인 *dump를 복사해서 넣어주는 과정을 정의해주는 것을 “깊은 복사”라고 한다.