우리는 객체를 생성할 때 정보은닉을 목적으로 멤버변수들을 private으로 선언했다. 따라서 멤버변수를 초기화하기 위해서는 따로 함수를 정의하고 호출하였다. 하지만 '생성자'를 이용하면 객체도 생성과 동시에 초기화할 수 있다.
생성자를 이해하기 위해 다음 예를 살펴보겠다.
class SimpleClass
{
private:
int num;
public:
SimipleClass(int n) //생성자(constructor)
{
num=n;
}
int GetNum() const
{
return num;
}
};
위 코드를 살펴보면 SimpleClass함수는 클래스의 이름과 이름이 동일하고 반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다.
이러한 유형을 '생성자(constructor)'라고 하며 객체 생성 시 한 번만 호출된다는 특징을 갖는다.
생성자를 정의하지 않았을 때, 우리는 다음과 같은 방식을 객체를 생성하였다.
SimpleClass sc;
SimpleClass *ptr=new SipleClass;
하지만 생성자가 정의되어있을 경우, 객체생서과정에서 자동으로 호출되는 생성자에게 전달할 인자의 정보를 다음과 같이 추가해야 한다.
SimpleClass sc(20);
SimipleClass * ptr = new SimpleClass(30);
생성자는 함수의 일종이기 때문에 오버로딩이 가능하고 매개변수에 디폴트 값을 설정할 수 있다. 아래 코드를 살펴보자.
#include <iostream>
using namespace std;
class SimpleClass{
private:
int num1;
int num2;
public:
SimpleClass(){
num1=0;
num2=0;
}
SimpleClass(int n){
num1=n;
num2=0;
}
SimpleClass(int n1, int n2){
num1=n1;
num2=n2;
}
/*SimpleClass(int n1=0, int n2=0){ //매개변수의 디폴트 값 설정
num1=n1;
num2=n2;
}*/
void ShowData() const{
cout<<num1<<' '<<num2<<endl;
}
};
int main(void){
SimpleClass sc1;
sc1.ShowData();
SimpleClass sc2(100);
sc2.ShowData();
SimpleClass sc3(100,200);
sc3.ShowData();
return 0;
}
결과
0 0
100 0
100 200
위의 코드를 보면 생성자는 오버로딩이 가능하다는 것을 알 수 있다. 또한 매개변수의 디폴트 값까지 설정할 수 있다는 것을 보여준다.
하지만 위의 코드에서 SimpleClass(int n1, int n2)와 SimpleClass(int n1=0, int n2=0) 은 매개변수의 형태와 인자의 수가 같기 때문에 객체를 생성할 때 어떤 생성자를 호출할지 애매해진다. 따라서 오버로딩의 특성에 맞춰 인자의 개수나 매개변수의 형태를 다르게 해야 오류가 나지 않는다.
(SimpleClass() 이렇게 정의된 생성자를 이용하여 객체를 생성하기 위해서는 SimpleClass s1(); 과 같이 생성하면 에러가 발생한다 따라서 SimpleClass s1; 이런 식으로 생성해야 한다.)
디폴트 생성자(Default Constructor)
메모리 공간의 할당 이우헤 생성자의 호출까지 완료되어야 객체라 할 수 있다. 즉, 객체가 되기 위해서는 반드시 하나의 생성자가 호출되어야 한다. 그리고 이러한 기준에 예외를 두지 않기 위해서 생성자를 정의하지 않는 클래스에는 C++ 컴파일러에 의해서 디폴트 생성자라는 것이 자동으로 삽입된다. 그런데 디폴트 생성자는 인자를 받지 않으며, 내부적을 아무런 일도 하지 않는 생성자이다.
ex1)
class AAA{
private:
int num;
public:
int GetNum {return num;}
};
ex2)
class AAA{
private:
int num;
public:
AAA(){} //디폴트 생성자
int GetNum{return num;}
};
예제 1번에는 생성자를 정의하지 않았지만 예제 2와 같이 기본적으로 디폴트 생성자가 자동으로 삽입된다 따라서 예제 1과 예제 2는 기능적으로 같은 코드이다.
하지만 여기서 주의해야 할 점은 생성자가 하나도 정의되어 있지 않을 때에만 디폴트 생성자가 삽입된다.
예를 들어 SoSimple(int n) : num(n) {}과 같은 생성자가 선언이 되어있으면 디폴트 생성자가 삽입되지 않으므로 SoS example; 과 같은 객체 생성은 불가능하다. (SoSimple(int n) : num(n) {} 은 예시로 SoSimple example(10); 이런 식으로 선언해야 한다.)
private 생성자
클래스 내부에서만 객체의 생성을 허용하려는 목적으로 생성자를 private으로 선언하기도 한다.
#include <iostream>
using namespace std;
class AAA{
private:
int num;
public:
AAA() : num(0) {}
AAA& CreateInitObj(int n) const{
AAA *ptr=new AAA(n);
return *ptr;
}
void ShowNum() const { cout<<num<<endl;}
private:
AAA(int n) : num(n) {}
};
int main(void){
AAA base;
base.ShowNum();
AAA &obj1=base.CreateInitObj(3);
obj1.ShowNum();
AAA &obj2=base.CreateInitObj(12);
obj2.ShowNum();
delete &obj1;
delete &obj2;
return 0;
}
결과
0
3
12
위 예제에서는 힙 영역에 생성된 객체를 참조의 형태로 반환하고 있다. 이는 힙에 할당된 메모리 공간은 변수로 간주하여, 참조자를 통한 참조가 가능하다는 것을 알 수 있다. 그리고 위 예제에서는 단순히 private으로 선언된 생성자를 통해서도 객체의 생성이 가능함을 보였다. private생성자는 특히 객체의 생성방법을 제한하고자 하는 경우에 매우 유용하게 사용이 된다.
소멸자의 이해와 활용
객체생성 시 반드시 호출되는 것이 생성자라면, 객체소멸 시 반드시 호출되는 것은 소멸자이다. 소멸자는 다음의 형태를 갖는다.
- 클래스의 이름 앞에'~'가 붙은 형태의 이름을 갖는다.
- 반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다.
- 매개변수는 void형으로 선언되어야 하기 때문에 오버로딩도, 디폴트 값 설정도 불가능하다.
소멸자는 ex)~AAA() {....} 이런 식으로 선언하고 객체소멸 과정에서 자동으로 호출된다. 프로그래머가 직접 소멸자를 정의하지 않으면, 디폴트 생성자와 마찬가지로 아무런 일도 하지 않는 디폴트 소멸자가 자동으로 삽입된다.
class AAA{
};
class AAA{
public:
AAA() {}
~AAA() {}
};
따라서 위 두 개의 코드는 같은 코드이다.
이러한 소멸자는 대개 생성자에서 할당한 리소스의 소멸에 상용된다. 예를 들어서 생성자 내에서 new연산자를 이용해서 할당해 놓은 메모리 공간이 있다면, 소멸자에서는 delete 연산자를 이용해서 이 메모리 공간을 소멸한다.
#include <iostream>
#include <cstring>
using namespace std;
class Person{
private:
char *name;
int age;
public:
Person(char *myname, int myage){
int len=strlen(myname)+1;
name=new char[len];
strcpy(name, myname);
age=myage;
}
void ShowPersonInfo() const{
cout<<"이름: "<<name<<endl;
cout<<"나이: "<<age<<endl;
}
~Person(){
delete []name;
cout<<"called destructor!"<<endl;
}
};
int main(void){
Person man1("Lee dong woo", 29);
Person man2("Jang ondg gun",41);
man1.ShowPersonInfo();
man2.ShowPersonInfo();
return 0;
}
결과
이름: Lee dong woo
나이: 29
이름: Jang ondg gun
나이: 41
called destructor!
called destructor!
위 예제를 보면 소멸자를 통해서 객체소멸과정에서 처리해야 할 일들을 자동으로 처리할 수 있다.
'c++' 카테고리의 다른 글
[C++]객체 배열과 객체 포인터 배열 (0) | 2023.03.01 |
---|---|
[C++]멤버 이니셜라이저(Member Initializer)를 이용한 멤버 초기화 (0) | 2023.02.27 |
[C++]캡슐화 (0) | 2023.02.19 |
[C++] Const 함수 (0) | 2023.02.18 |
[C++]정보은닉(information Hiding) (0) | 2023.02.18 |