생성자와 소멸자 2

 

인프런 Rookiss님의 'Part1: C++ 프로그래밍 입문' 강의를 기반으로 정리한 필기입니다. 
😎[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 강의 들으러 가기!

 

 


 

기본 생성자를 만들지 않는 경우

 

#include <iostream>
using namespace std;

class Knight
{
public:
    Knight(int hp)
    {
        cout << "Knight(int) 기본 생성자 호출" << endl;

        _hp = hp;
        _attack = 10;
        _posX = 0;
        _posY = 0;
    }

    // 소멸자
    ~Knight()
    {
        cout << "Knight() 소멸자 호출" << endl;
    }

    // 멤버 함수 선언
    void Move(int y, int x);
    void Attack();
    void Die()
    {
        _hp = 0;
        this->_hp = 1;  // this는 자기 자신을 가리킨다
        cout << "Die" << endl;
    }

public:
    // 멤버 변수
    int _hp;
    int _attack;
    int _posY;
    int _posX;
};


void Knight::Move(int y, int x)
{
    _posY = y;
    _posX = x;
    cout << "Move" << endl;
}

void Knight::Attack()
{
}

int main()
{
    Knight k1;
    k1._hp = 100;
    k1._attack = 10;
    k1._posY = 0;
    k1._posX = 0;

    return 0;
}

 

실행화면

 

하지만 아래와 같이 고치면 잘 실행된다.

int main()
{
    Knight k1(100);
    k1._hp = 100;
    k1._attack = 10;
    k1._posY = 0;
    k1._posX = 0;

    return 0;
}
  • Knight k1(100)으로 변경되어 Knight(int hp)가 호출된다.

 

 

 

연습 예시

 

#include <iostream>
using namespace std;

class Knight
{
public:
    //// [1] 기본 생성자 (인자가 없음)
    //Knight()
    //{
    //    cout << "Knight() 기본 생성자 호출" << endl;

    //    _hp = 100;
    //    _attack = 10;
    //    _posX = 0;
    //    _posY = 0;
    //}

    //// [2] 복사 생성자 : 자기 자신의 클래스 참조 타입을 인자로 받음.
    //// 일반적으로 '똑같은' 데이터를 지닌 객체가 생성되길 기대한다
    //Knight(const Knight& knight)
    //{
    //    _hp = knight._hp;
    //    _attack = knight._attack;
    //    _posX = knight._posX;
    //    _posY = knight._posY;
    //}

    Knight(int hp)
    {
        cout << "Knight(int) 기본 생성자 호출" << endl;

        _hp = hp;
        _attack = 10;
        _posX = 0;
        _posY = 0;
    }

    // 소멸자
    ~Knight()
    {
        cout << "Knight() 소멸자 호출" << endl;
    }

    // 멤버 함수 선언
    void Move(int y, int x);
    void Attack();
    void Die()
    {
        _hp = 0;
        this->_hp = 1;  // this는 자기 자신을 가리킨다
        cout << "Die" << endl;
    }

public:
    // 멤버 변수
    int _hp;
    int _attack;
    int _posY;
    int _posX;
};


void Knight::Move(int y, int x)
{
    _posY = y;
    _posX = x;
    cout << "Move" << endl;
}

void Knight::Attack()
{
}

int main()
{
    Knight k1(100);
    // k1._hp = 100;
    k1._attack = 10;
    k1._posY = 0;
    k1._posX = 0;

    Knight k2(k1);

    k1.Move(2, 2);
    k1.Attack();
    k1.Die();

    return 0;
}

 

실행화면


 

 

 

명시적인 용도로만 사용  -   explicit 키워드 

 

#include <iostream>
using namespace std;

class Knight
{
public:
    // [1] 기본 생성자 (인자가 없음)
    Knight()
    {
        cout << "Knight() 기본 생성자 호출" << endl;

        _hp = 100;
        _attack = 10;
        _posX = 0;
        _posY = 0;
    }

    // [2] 복사 생성자 : 자기 자신의 클래스 참조 타입을 인자로 받음.
    // 일반적으로 '똑같은' 데이터를 지닌 객체가 생성되길 기대한다
    /*Knight(const Knight& knight)
    {
        _hp = knight._hp;
        _attack = knight._attack;
        _posX = knight._posX;
        _posY = knight._posY;
    }*/

	// [3] 기타 생성자
    // 이 중에서 인자를 1개만 받는 [기타 생성자]를
    // [타입 변환 생성자]라고 부르기도 함
    
    // 명시적인 용도로만 사용할 것! explicit
    explicit Knight(int hp)
    {
        cout << "Knight(int) 기본 생성자 호출" << endl;

        _hp = hp;
        _attack = 10;
        _posX = 0;
        _posY = 0;
    }

    Knight(int hp, int attack, int posX, int posY)
    {
        _hp = hp;
        _attack = 10;
        _posX = 0;
        _posY = 0;
    }

    // 소멸자
    ~Knight()
    {
        cout << "Knight() 소멸자 호출" << endl;
    }

    // 멤버 함수 선언
    void Move(int y, int x);
    void Attack();
    void Die()
    {
        _hp = 0;
        this->_hp = 1;  // this는 자기 자신을 가리킨다
        cout << "Die" << endl;
    }

public:
    // 멤버 변수
    int _hp;
    int _attack;
    int _posY;
    int _posX;
};


void Knight::Move(int y, int x)
{
    _posY = y;
    _posX = x;
    cout << "Move" << endl;
}

void Knight::Attack()
{
    cout << "Attack : " << _attack << endl;
}

void HelloKnight(Knight k)
{
    cout << "Hello Knight" << endl;
}

int main()
{
    Knight k1(100, 10, 0, 0);
    // k1._hp = 100;
    k1._attack = 10;
    k1._posY = 0;
    k1._posX = 0;

    Knight k2(k1);     // 복사 생성자로 만들어진다.
    Knight k3 = k1;  // 복사 생성자로 만들어진다.

    Knight k4;   // 생성자
    k4 = k1;     // 복사

    k1.Move(2, 2);
    k1.Attack();
    k1.Die();

    // 암시적 형변환 -> 컴파일러가 알아서 바꿔치기
    int num = 1;

    float f = (float)num; // 명시적 < 우리가 코드로 num을 float 바구니에 넣으라고 주문하고 있음
    // float f = num;     // 암시적 << 별말 안 했는데 컴파일러가 알아서 처리하고 있음
    double d = num; // 암시적 << 별말 안 했는데 컴파일러가 알아서 처리하고 있음

    Knight k5;
    k5 = (Knight)1;  // 명시적. 위에 explicit를 붙였기 때문에 명시적으로 사용해야만 함.

    HelloKnight((Knight)5); // 명시적. 위에 explicit를 붙였기 때문에 명시적으로 사용해야만 함.

    return 0;
}

 

 

핵심부분

 	explicit Knight(int hp)
    {
        cout << "Knight(int) 기본 생성자 호출" << endl;

        _hp = hp;
        _attack = 10;
        _posX = 0;
        _posY = 0;
    }
int main()
{
    float f = (float)num; // 명시적 < 우리가 코드로 num을 float 바구니에 넣으라고 주문하고 있음
    // float f = num;     // 암시적 << 별말 안 했는데 컴파일러가 알아서 처리하고 있음
    double d = num; // 암시적 << 별말 안 했는데 컴파일러가 알아서 처리하고 있음

    Knight k5;
    k5 = (Knight)1;  // 명시적. 위에 explicit를 붙였기 때문에 명시적으로 사용해야만 함.

    HelloKnight((Knight)5); // 명시적. 위에 explicit를 붙였기 때문에 명시적으로 사용해야만 함.

    return 0;
}

 

 

 

 

 

 

 

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <iostream>
using namespace std;
 
class Monster
{
private:
    string Name    = "허수아비";
    int Hp        = 10000;
 
public:
    void Damaged(int atk)
    {
        printf("%i 데미지를 입었습니다. \n", atk);
        Hp -= atk;
        printf("남은 Hp : %i \n", Hp);
    }
};
 
class Character
{
protected:
    string        Name= "";
    unsigned    Lv    = 0;
    unsigned    Hp    = 0;
    unsigned    Atk = 0;
 
public:
    Character() { cout << "캐릭터 생성자 호출" << endl; }
    // 생성자 리스트
    // 생성자를 실행하기 전에 실행할 명령을 등록합니다.
    // 생성자 뒤에 : 을 작성하여 사용합니다.
    // 받은 인자 값으로 멤버들을 먼저 초기화 시킨 후 생성자의 정의를 실행합니다.
    Character(string name, unsigned lv, unsigned hp, unsigned atk) : Name(name), Lv(lv), Hp(hp), Atk(atk)
    {
        Name    = name;
        Lv        = lv;
        Hp        = hp;
        Atk        = atk;
    }
    ~Character() { cout << "캐릭터 소멸자 호출" << endl; }
 
public:
    void PrintCharacter() const
    {
        cout << "Name : " << Name << endl;
        cout << "Lv : " << Lv << endl;
        cout << "Hp : " << Hp << endl;
        cout << "Atk : " << Atk << endl;
    }
 
    int Attack()
    {
        printf("%s가 공격합니다. \n", Name.c_str());
        return Atk;
    }
};
 
class Warrior : public Character
{
private :
    int STR;
 
public:
    Warrior() { cout << "워리어 생성자 호출" << endl; }
    // 상속에서 생성자는 최상위 기반클래스부터 최하위 파생 클래스 순서로 호출됩니다.
     
    // 생성자 리스트
    // 생성자를 실행하기 전에 실행할 명령을 등록합니다.
    // 생성자 뒤에 : 을 작성하여 사용합니다.
    // 기반 클래스의 생성자를 지정할 수 있습니다.
    Warrior(string name, unsigned lv, unsigned hp, unsigned atk, int str) : Character(name, lv, hp, atk)
    {
        STR = str;
    }
 
    ~Warrior() { cout << "워리어 소멸자 호출" << endl; }
    // 상속에서 소멸자는 최하위 기반클래스부터 최상위 기반 클래스 순서로 호출됩니다.
 
public:    
    int Skill()
    {
        printf("%s가 스킬을 사용합니다. \n", Name.c_str());
        return Atk + STR;
    }
 
    // 기반 클래스의 매서드를 재정의 합니다.
    // 이를 오버라이딩이라 합니다.
    void PrintCharacter() const
    {
        // Warrior 형식에선 Character 형식의 구조를 가지고 있고
        // Warrior 의 PrintCharacter가 아닌
        // Character 형식에 있는 PrintCharacter를 호출해줄 수 있습니다.
        Character::PrintCharacter();
        cout << "STR : " << Atk << endl;
    }
};
 
 
int main()
{
    //character character = character("캐릭터", 1, 10, 10);
    //character.printcharacter();
 
    // Warrior 형태의 객체를 동적할당하고 동적할당한 주소를 저장합니다.
    //Warrior warrior = new Warrior("워리어", 1, 100, 10, 1);
    //warrior.PrintCharacter();
    //warrior.Attack();
    //warrior.Skill();
    // warrior.Lv // protected로 설정한 멤버이므로 상속받은 클래스가 아닌 외부에서 사용 불가
 
    // 업 캐스팅 : 파생 클래스의 형식을 기반 클래스의 형시긍로 가리키는 것을 말합니다.
    Character* warrior = new Warrior("워리어"1100101);
    warrior->PrintCharacter();
    ((Warrior*)warrior)->PrintCharacter();
 
    // 다운 캐스팅 : 기반 클래스의 형식을 파생 클래스의 형시긍로 가리키는 것을 말합니다.
    //Character* character = new Character("워리어", 1, 100, 10, 1);
    //Warrior* down = (Warrior*)character;
    //down->PrintCharacter();
 
    delete warrior;
    warrior = nullptr;
 
    return 0;
};
cs

 

'⭐ Programming > C++' 카테고리의 다른 글

[C++] 은닉성  (0) 2022.04.04
[C++] 상속성  (0) 2022.04.04
[C++] 생성자와 소멸자  (0) 2022.04.04
[C++] 객체지향  (0) 2022.04.03
[C++] 파일 분할 관리, #ifndef, #endif  (0) 2022.04.03