C,C++/CONCEPT

C++ Chapter 04. 클래스의 완성. 04-3 생성자(Constructor)와 소멸자(Destructor)

너래쟁이 2018. 2. 28. 18:25

C++ Chapter 04. 클래스의 완성. 


04-3 생성자(Constructor)와 소멸자(Destructor)


* 생성자의 이해

// 생성자는 객체 생성과 동시에 초기화 할 수 있다.

// 클래스의 이름과 함수의 이름이 동일하다.

// 반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다

// 생성자도 함수의 일종이니 오버로딩이 가능하다

// 생성자도 함수의 일종이니 매개변수에 '디폴트 값'을 설정 할 수 있다.




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
42
43
44
45
46
47
48
49
50
51
#include <iostream>
using namespace std;
 
class SimpleClass
{
    int num1;
    int num2;
 
public:
    SimpleClass()
    {
        num1=0;
        num2=0;
    }
    SimpleClass(int n) // n
    {
        num1=n;
        num2=0;
    }
    SimpleClass(int n1, int n2) // 100, 200
    {
        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(100200);
    sc3.ShowData();
    return 0;
}
cs



* 생성자의 함수적 특성
// 생성자도 함수의 일종이므로 오버로딩이 가능하다



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 SimpleClass
{
    int num1;
    int num2;
 
public:
    SimpleClass(int n1=0int n2=0)
    {
        num1=n1;
        num2=n2;
    }
 
    void ShowData() const
    {
        cout<<num1<<' '<<num2<<endl;
    }
};
 
int main(void)
{
    SimpleClass sc1();    // 함수의 원형 선언! (29행에 정의되어 있는 함수를 호출하기 위해)
 
    SimpleClass mysc=sc1();
    mysc.ShowData();
    return 0;
}
 
SimpleClass sc1()
{
    SimpleClass sc(2030);
    return sc;
}
cs



Point, Rectangle 클래스에 생성자 적용



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



* 객체의 생성과정

// 이니셜라이저가 선언되지 않았다면, 1단계 : 메모리 공간의 할당, 3단계 : 생성자의 몸체부분의 실행으로 객체생성은 완성된다

// 그러나 생성자는 이니셜라이저처럼 선택적으로 존재하는 대상이 아니다.

// 생성자는 반드시 호출이 된다. 우리가 생성자를 정의하지 않으면, '디폴트 생성자(default constructor)'라는 게 자동으로 삽입되어 호출이 된다




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

// '멤버 이니셜라이저'는 객체가 아닌 멤버의 초기화에도 사용할 수 있다.



// 1. 프로그래머는 생성자의 몸체에서 초기화 하는 방법, 

// 2. 이니셜라이저를 이용하는 초기화 방법 중에서 선택이 가능하다


// 일반적으로 멤버변수의 초기화에 있어서는 2. 이니셜라이저를 선호하는 편이다. 두 가지 이점이 있기 때문이다.

1. 초기화의 대상을 명확히 인식할 수 있다. 2. 성능에 약간의 이점이 있다.


// const 멤버변수도 이니셜라이저를 이용하면 초기화가 가능하다!


* 이니셜라이저의 이러한 특징은 멤버변수로 참조자를 선언할 수 있게 합니다

// const 변수와 마찬가지로 '참조자'도 선언과 동시에 초기화가 이뤄져야 한다.

// 따라서 이니셜라이저를 이용하면 참조자도 멤버변수로 선언될 수 있다.

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
42
#include <iostream>
using namespace std;
 
class AAA
{
public:
    AAA()
    {
        cout<<"empty object"<<endl;
    }
    void ShowYourName()
    {
        cout<<"I'm class AAA"<<endl;
    }
};
 
class BBB
{    
private:
    AAA &ref;
    const int &num;
 
public:
    BBB(AAA &r, const int &n)
        : ref(r), num(n)
    {
    }     
    void ShowYourName()
    {
        ref.ShowYourName();
        cout<<"and"<<endl;
        cout<<"I ref num "<<num<<endl;
    }
};
 
int main(void)
{
    AAA obj1;
    BBB obj2(obj1, 20);
    obj2.ShowYourName();
    return 0;
}
cs





* 디폴트 생성자(Default Constructor)

// 매개변수가 void형으로 선언된 디폴트 생성자는, 생성자가 하나도 정의되어 있지 않을 때에만 삽입이 된다.

// 메모리 공간의 할당 이후에 생성자의 호출까지 완료되어야 '객체'라 할 수 있다.

// 즉, 객체가 되기 위해서는 반드시 하나의 생성자가 호출되어야 한다


AAA * ptr = new AAA; // 이런 형태로 생성해도 객체의 생성과정에서 생성자가 호출된다

AAA * ptr = (AAA*)malloc(sizeof(AAA)); // malloc 함수를 대신 이용하면 생성자는 호출되지 않는다

// 실제로는 AAA 클래스의 크기정보만 바이트 단위로 전달되기 때문에 생성자가 호출될 리 없다.

// 따라서 객체를 동적으로 할당하려는 경우에는 반드시 new 연산자를 이용해야 한다



* 생성자 불일치



* private 생성자

// 객체의 생성이 클래스의 외부에서 진행되면, 생성자는 public으로 선언되어야 한다

// 객체의 생성이 클래스의 내부에서 진행되면, 생성자는 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
36
#include <iostream>
using namespace std;
 
class AAA
{
private:
    int num;
 
public:
    AAA() : num(0) {}
    AAA& CreateInitObj(int n) const
    {
        AAA * ptr=new AAA(n); // 클래스 내부에서는 private 생성자의 호출이 가능하다

        return *ptr;
    }
    void ShowNum() const { cout<<num<<endl; }
 
private:
    AAA(int n) : num(n) {}

// 생성자가 private이므로 클래스 외부에서는 이 생성자의 호출을 통해서 객체 생성이 불가능하다


};
 
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;
}
cs



* 소멸자의 이해와 활용

// 객체생성시 반드시 호출되는 것이 생성자라면,

// 객체소멸시 반드시 호출되는 것은 소멸자이다.


1. 반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다

2. 매개변수는 void형으로 선언되어야 하기 때문에 오버로딩도, 디폴트 값 설정도 불가능하다.


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
#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 dong gun"41);
    man1.ShowPersonInfo();
    man2.ShowPersonInfo();
    return 0;
}
cs