코딩하는 오징어

C++의 클래스 생성자 & 소멸자 본문

Language/C++

C++의 클래스 생성자 & 소멸자

코딩하는 오징어 2017. 10. 9. 21:21
반응형

 안녕하세요. 코딩하는 오징어입니다. 오늘은 C와 C++의 차이점인 클래스에 대해서 포스팅하겠습니다! 드디어 클래스 단계까지 왔습니다.  객체지향언어에서 클래스는 상당히 중요한 자리를 차지하고 있죠!

먼저 용어를 좀 정리하고 시작하겠습니다.


첫 번째로 OOP에서 클래스와 객체는 다른 의미를 가진 단어입니다. 클래스는 객체를 만들기 위한 설계도이고 이 설계도를 가지고 공간을 할당 받아 실체화 한 것이 객체입니다. 즉 어떤 특정한 객체에 대한 클래스는 하나라고 할 수 있고 어떤 클래스에대한 객체는 여러개라고 말 할 수 있습니다. 설계도 하나 가지고 여러개의 똑같은 것을 찍어 낼 수 있으니까요. 


두 번째로 클래스안에는 멤버 변수와 멤버 함수를 선언할 수 있습니다. 멤버변수는 C의 구조체에도 있지만 멤버 함수는 클래스에 추가된 기능입니다. 구체적인 것은 용어 설명 뒤에 예제 코드로 알아 보겠습니다.


세 번째로 클래스를 생성 할 때 접근제어자라는 것이 존재 합니다. 접근 제어자는 private, protected, public 이 세 가지가 존재하는데요 protected는 클래스 상속을 위한 접근제어자이기 때문에 이번 글에서는 설명하지않고 클래스 상속에대한 개념을 포스팅할 때 설명해 드리겠습니다. 의미그대로 private으로 접근제어자를 설정하면 클래스 내에서만 멤버 변수나 멤버 함수에 접근 할 수 있습니다. public으로 멤버 변수나 멤버 함수를 선언하면 어디에서든 접근 할 수 있다는 의미입니다.


네 번째로 클래스에는 생성자와 소멸자가 있습니다. 클래스를 가지고 객체를 생성 할 때 (클래스의 인스턴스화라고 함) 자동으로 이 생성자를 호출하고 클래스가 소멸될 때 소멸자가 자동으로 호출 됩니다.


이제 예제 코드를 보면서 공부해 봅시다. 먼저 C언어의 구조체로 클래스를 흉내내보도록 하겠습니다.



 위 struct Person구조체를 보시면 멤버 변수로 함수 포인터가 있는 것을 확인 할 수 있습니다. 멤버 함수의 기능이 구조체에는 없기 때문에 함수 포인터를 이용하여 흉내를 낸 것입니다. 왜 굳이 흉내를 냈냐고 물어보시면 아주 중요하지만 쉽게 지나치는 개념이 있기 때문입니다. 어떤 분이 클래스에 멤버 함수를 넣고 많은 객체들을 만들어 내면 메모리를 많이 잡아 먹지 않겠느냐고 했던 적이 있었습니다. 하지만 클래스의 멤버 함수는 위의 코드처럼 실행이 됩니다. 클래스안의 멤버함수들은 객체를 생성할 때 한 번만 생성됩니다. 한 번 생성 되고 나면 여러 객체들이 처음 생성된 함수의 포인터를 공유하는 것입니다. 말로는 추상적이기 때문에 코드로 보시면 더 이해가 잘 되기에 흉내를 내보았습니다. 그럼 이제 위에 코드를 C++의 클래스로 구현해 봅시다.



한 줄 한 줄 해석 해보겠습니다. 먼저 클래스를 선언(정의와 선언은 다릅니다. 정의는 선언후에 따로 하는 것이 좋습니다.) 하기위한 키워드는 class입니다.


class 클래스명 { 


};

이런 식으로 클래스를 만듭니다. 클래스 안에 private, public이 보이시나요? 용어 정리하면서 말씀드린 접근 제어자입니다. 보통 클래스의 멤버변수들은 private으로 외부에서의 접근을 막습니다. OOP의 은닉성이라는 특징을 지키기 위해 서입니다. private변수들은 getter와 setter함수로 (accessor와 mutator라고도 함) 간접적으로 접근해야합니다. 따라서 이 함수들은 접근제어 지시자를 public으로 정해야합니다. getter와 setter함수는 public 위치에 있는 함수들 중 GetXXX(), SetXXX()가 getter함수와 setter함수 입니다. 


생성자


Person(); //기본생성자

Person(string name, int age); //생성자 오버로딩


코드를 보시면 위와 같은 함수가 보일 겁니다. 이것이 바로 생성자인데요. 생성자는 함수라고 생각하시면 됩니다. 다만 일반 함수들과는 조금 다른 것이 있다면 return 값을 적어주지 않습니다. 주로 책에서는 return 값이 없다고 설명 하는데 저는 이부분이 조금 조심스럽습니다. 실제로 생성자는 생성된 객체의 참조값을 return합니다. 객체를 생성하려면 생성자가 무조건 호출되어야하는 이유이지요. 생성자는 보시는 것과 같이 규칙이있습니다. 바로 클래스명과 같은 이름으로 만들어야합니다. (parameter는 상관없습니다.)

 생성자도 함수이기 때문에 오버로딩이 가능합니다. 생성자를 따로 정의 하지않으면 자동으로 기본생성자를 컴파일러가 만들어 줍니다. 하지만 생성자를 하나라도 오버로딩 한다면 기본생성자는 개발자가 직접 넣어주어야합니다.


소멸자


~Person();


 코드를 보시면 위와 같은 함수가 있습니다. 이것은 소멸자입니다. 소멸자도 함수입니다. 소멸자는 return값이 없다고 할 수 있습니다. 소멸자는 객체가 소멸할 때 호출되는 함수입니다. 소멸자도 따로 정의하지않으면 기본 소멸자를 컴파일러가 넣어줍니다. 하지만 클래스안의 동적으로 할당된 멤버변수가 있다면 소멸자안에 메모리를 풀어주는 코드를 넣어 주어야 합니다.



클래스의 정의 부분입니다. 클래스를 정의할 때는 return type 클래스명::멤버함수명(parameter) 형태로 멤버함수들을 정의합니다.(:: 범위지정 연산자 namespace 부분을 보시면 기억 하실 수 있습니다.) 클래스도 어떤 범주이기 때문에 범주안의 멤버함수들을 정의한다라고 명시적으로 알려주는 것입니다. 


Person::Person(string name, int age) : name(name), age(age) {

cout << "생성자 오버로딩" << endl;

}


위의 코드를 클래스의 정의 부분에서 보실 수 있습니다. 빨간색 부분을 보시면 이게 뭐야? 라는 질문을 하실 수 있습니다. 위 부분은 이니셜라이져라고 하는 C++의 문법입니다. 생성자를 이용하여 멤버변수를 초기화할 때는 이니셜라이져라는 것을 사용하는 것을 권장 합니다. C++은 변수를 생성과 동시에 초기화 할때 ()를 이용하여 할 수 있습니다. name(name)에서 괄호 밖name은 멤버변수의 name이고 괄호안 name은 Person생성자함수의 parameter name입니다.


this라는 키워드를 볼 수 있는데 this는 객체 자기자신의 주소값을 가지고 있습니다 .변수 naming도 굉장히 번거로운 일이기 때문에 이런식으로 귀찮은 수고를 덜 수 있습니다.


그럼 이제 우리가 만든 클래스를 가지고 test를 해보겠습니다.



main함수를 보시는 것과 같이 저런식으로 클래스에대한 객체를 생성 하시면 됩니다. 동적으로 생성하고 싶으실 때는


Person *p = new Person("코딩하는 오징어", 24);


이렇게 new연산을 이용하시면 생성자를 호출하여 참조값을 반환해 줍니다. 그것을 변수에 담으면 됩니다. 물론 변수는 포인터 변수이므로 ->연산을 사용하여 멤버함수나 변수에 접근하여야합니다.


결과를 보시면 두개의 객체가 생성됐으니 두개의 생성자가 호출됩니다. 인자를 아무것도 주지않으면 기본생성자가 호출되는 것을 확인 할 수 있습니다. 끝에는 두 개의 소멸자가 호출 됐습니다.  생성자와 소멸자는 중요한 요소이므로 여기서 기본을 다지시고 실제로 코딩하면서 손에 익혀두시는 것이 중요합니다. 따라서 예제 소스코드를 직접 사용하 실 수 있게 git주소를 올려드리겠습니다. 꼭 출력결과를 확인해보고 생성자와 소멸자 함수를 오버로딩 해보면서 손에 익히세요!! 


클래스를 흉내낸 구조체 코드: https://github.com/gksxodnd007/autumn_tutor_CPP/blob/master/struct_on_cpp.cpp

Person 클래스 코드 : https://github.com/gksxodnd007/autumn_tutor_CPP/blob/master/Person.cpp



반응형
Comments