개발/C, C++

[C++] 생성자(Constructor)와 소멸자(Destructor)

huiyu 2018. 12. 16. 13:37

- 생성자(Constructor)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class SimpleClass
{
private:
  int num;
public:
  SimpleClass(int n) // 생성자(Constructor)
  {
    num = n;
  }
  int GetNum() const
  {
    return num;
  }
};
cs

 - 위 클래스의 SimpleClass()는 생성자(Constructor)이며, 아래와 같은 특징을 지닌다.
  1) 클래스의 이름과 함수의 이름이 동일
  2) 반환형이 선언되지 않으며, 실제로 반환되지 않는다.
  3) 생성자(Constructor)는 객체 생성시 딱 한번만 호출된다.
  4) 생성자도 함수의 일종으로 오버로딩이 가능하다.
  5) 생성자도 함수의 일종으로 매개변수에 '디폴트 값'을 설정할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#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;
  }
};
cs

- '멤버 이니셜라이저(Member Initializer)'를 이용한 멤버 초기화

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Rectangle
{
  private:
    Point upLeft;
    Point lowRight;
 
  public:
    Rectangle(const in &x1, const int&y1, const int &x2, const int &y2);
    void ShowRecInfo() const;
}
 
Rectnagle::Rectangle(const in &x1, const int&y1, const int &x2, const int &y2)
             : upLeft(x1, y1), lowRight(x2, y2)
{
    //empty
}
cs

 -> : upLeft(x1, y1), lowRight(x2, y2)
   이는 '멤버 이니셜라이저'이다.
  1) 객체 upLeft 생성과정에서 x1과 y1을 인자로 전달받는 생성자를 호출
  2) 객체 lowRight의 생성과정에서 x2와 y2을 인자로 전달받는 생성자를 호출

- '멤버 이니셜라이저(Member Initalizer)'를 이용한 변수 및 const 상수(변수) 초기화

1
2
3
4
5
6
7
8
9
10
11
12
class SoSimple
{
private:
  int num1;
  int num2;
 
public:
  SoSimple(int n1, int n2) : num1(n1)
  {
    num2 = n2;
  }    
};
cs

  -> 객체가 아닌 멤버변수도 이니셜라이저를 통해서 초기화가 가능하다. num1(n1)

  프로그래머는 생성자의 몸체에서 초기화 하는 방법과 이니셜라이저를 이용하는 초기화 방법 중에서 선택이 가능하다.
 그러나 일반적으로 멤버변수의 초기화에 있어서는 이니셜라이저를 선호하는 편이다. 이는 아래와 같은 이점이 있다.
  1) 초기화의 대상을 명확히 인식
  2) 성능에 약간의 이점

** 이니셜라이저를 이용하면 선언과 동시에 초기화가 이뤄지는 형태로 바이너리 코드가 생성된다.
  반면, 생성자의 몸체부분에서 대입연산을 통한 초기화를 진행하면, 선언과 초기화를 각각 별도의 문장에서 진행하는 형태로 바이너리 코드가 생성된다.

 -> const 변수는 선언과 동시에 초기화하여야 하는데, 이니셜라이저를 이용한다면 const멤버변수도 초기화가 가능하다!
 마찬가지로, 선언과 동시에 초기화가 이뤄져야하는 '참조자' 역시 초기화가 가능하다.


- 디폴트 생성자(Default Constructor)

 일반적으로 객체의 실행은 아래와 같은 순서로 진행된다.
 1) 메모리 공간의 할당
 2) 이니셜라이저를 이용한 멤버변수(객체)의 초기화
 3) 생성자의 몸체부분 실행

 * 우리가 생성자를 정의하지 않으면, '디폴트 생성자(Default Constructor)'가 자동으로 삽입되어 호출된다.
 -> 객체가 되기 위해서는 반드시 하나의 생성자가 호출되어야 한다.
 이러한 기준에 예외를 두지 않기 위해 생성자를 정의하지 않는 클래스에는 C++ 컴파일러에 의해서 디폴트 생성자라는 것이 자동으로 삽입된다.

- 디폴트 생성자는 인자를 받지 않으며, 내부적으로 아무런 일도 않는 생성자이다.

1
2
3
4
5
6
7
8
class AAA
{
private:
  int num;
public:
  AAA() {} // 디폴트 생성자
  int GetNum { return num; }
};
cs

** 모든 객체는 한번의 생성자 호출을 동반한다. 이는 new 연산자를 이용한 객체의 생성에도 해당한다.
   AAA * ptr = new AAA;

* 그러나 malloc 함수를 이용하면, 생성자는 호출되지 않는다.
   AAA * ptr = (AAA*) malloc (sizeof(AAA));
 -> malloc 함수 호출 시, 실제로는 AAA클래스의 크기정보만 바이트 단위로 전달되기 때문에 생성자가 호출될리 없다.
  따라서 객체를 동적할당하려면 반드시 new 연산자를 이용해야 한다.

**new연산자가 하는 일은 아래사이트에서 읽고 도움이 되었습니다.
   C++ new 연산자의 진실(https://lunapiece.net/Article/3900)


- private 생성자

 : 클래스 내부에서만 객체의 생성을 허용하려는 목적으로 생성자를 private으로 선언하기도 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#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 bse;
  base.ShowNum();
 
  AAA &obj1 = base.CreateInitObj(3);
  obj1.ShowNum();
 
  AAA &obj2 = base.CreateInitObj(12);
  obj2.ShowNum();
 
  delete &obj1;
  delete &obj2;
  return 0;
}
cs

- 위 예제에서는 힙 영역에 생성된 객체를 참조의 형태로 반환하고 있다.
- 힙에 할당된 메모리 공간은 변수로 간주하여, 참조자를 통한 참조가 가능하다.
- 또한 private으로 선언된 생성자를 통해서도 객체의 생성이 가능함을 보여준다.


- 소멸자(Destructor)

 : 객체 생성시 반드시 호출되는 것이 생성자라면, 객체소멸시 반드시 호출되는 것은 소멸자이다.

 - 클래스 이름 앞에 '~'가 붙은 형태의 이름을 갖는다.
 - 반환형이 선언되어 있지 않으며, 실제로 반환되지 않는다.
 - 매개변수는 void형으로 선언되어야 하기 때문에 오버로딩이나 디폴트 값 설정이 불가능하다.

 ~AAA() { .... }

 -> 직접 소멸자를 정의하지 않으면 디폴트 생성자와 마찬가지로 아무런 일도 하지 않는 소멸자가 자동으로 삽입된다.


[참고자료]
윤성우 열혈 C++ 프로그래밍04-3 생성자(Constructor)와 소멸자(Destructor)


728x90
반응형

'개발 > C, C++' 카테고리의 다른 글

[C++] 복사 생성자(Copy Constructor)  (0) 2018.12.18
[C++] this 포인터  (0) 2018.12.16
[C++] 정보은닉(Information Hiding)  (0) 2018.12.15
[C++] 클래스(Class)와 객체(Object)  (0) 2018.12.15
[C++] C++에서의 구조체  (0) 2018.12.15