본문 바로가기
C++/기초(두들낙서)

[C++] 오버라이딩과 정적바인딩 / 가상함수와 동적바인딩

by Meaning_ 2022. 7. 10.
728x90
반응형

오버라이딩

 

d는 Base로부터 상속받았음에도 불구하고 d.a를 했을 때 10이 아니라 20이 출력된다.

이렇게 자식의 멤버를 부모클래스 보다 우선하는 것으로 오버라이딩이라고 한다.

(override:우선하다)

 

만약에 Base에 접근하려면 d.Base::a를 출력하면 된다.

 

정적바인딩

 

c++에서는 다른 클래스를 포인터로 가리킬 수 있는데

단 상속받았을 때만 가능하다. (부모를 가리키는 포인터가 자식을 가리킬때)

main함수를 보자. b는 Derived타입의 객체를 동적할당받았다. 그니까 그 주소가 b에 저장된 것이다.

이제 b에서 Print라는 멤버메서드를 호출하도록 하자.

근데 b는 포인터이기에 (*b).Print()를 하는데 이건 사실상 b->Print()와 같다. 우리가 화살표함수는 포인터를 통해 멤버에 접근할 때 사용했기 때문이다!

 

여기서 궁금증이 생긴다. b라는 애가 동적할당된 Derived내에서 Print함수를 호출할지

그냥 Base의 Print함수를 호출할지 말이다.

 

결과는 Base의 Print가 호출된다.

 

왜 그럴까? 컴파일러가 Base의 Print를 호출할지 Derived의 Print를 호출할지 책임을 전가하면

만약 코드가 길어질 때 컴파일러가 굉장히 힘들어질 수 있다.

 

바인딩이란

b라는 모호한 포인터를 가져다가 어떤 타입을 가리키지는지 정하는 것을 말한다.

 

정적바인딩은

b를 Base를 가리키는 애라고 확정하는 것이다. (Derived인지 Base인지 고민하지 않는다)

특히 상속의 경우 부모클래스가 자식클래스를 가리킬 수 있는데 이때 부모클래스를 통해 자식클래스의 멤버에 가리키면 

많이 상속할 수록 포인터가 가리키는 클래스의 종류도 많이 따져봐야 한다. 근데 정적바인딩은 그런 고민의 과정을 거치지 않고 그냥 부모클래스에 해당하는 애야! 라고 확정한다. 

 

 

그렇다면 반대로 동적바인딩은

b가 어떨때는 Derived가 될수도 있고 ,Base가 될수도 있다는 것이다.

 

C++은 기본적으로 정적바인딩이 일어난다.

 

가상함수

 

그러면 동적바인딩을 하기 위해서는 어떻게 해야할까?

부모 클래스의 멤버 함수에 virtual를 붙여주면 된다!

virtual이라는 것은 함수가 호출이 될 수도 있고, 안될 수도 있다는 것을 내포하고 있다.

즉, 프로그램을 만드는 시점에서는 알 수 없으니 프로그램이 실행되는 시점에서 결정하게 끔 하는것이다.

 

 

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
class Weapon {
 
protected:
    int power;
 
public:
    Weapon(int power) :power(power) {
        cout << "Weapon(int)" << endl;
    }
 
    virtual void Use() {
        cout << "Weapon::Use()" << endl;
    }
};
 
class Sword :public Weapon {
 
public:
    Sword(int power) :Weapon(power) {
        cout << "Sword(int)" << endl;
    }
 
    void Use() {
        cout << "Sword::Use()" << endl;
        Swing();
    }
 
private:
    void Swing() {
        cout << "Swing sword" << endl;
    }
};
 
class Magic :public Weapon {
 
public:
    Magic(int power, int manaCost) :Weapon(power), manaCost(manaCost) {
        cout << "Magic(int,int)" << endl;
    }
 
    void Use() {
        cout << "Magic::Use()" << endl;
        Cast();
    }
 
private:
    void Cast() {
        cout << "Cast magic" << endl;
    }
    int manaCost;
};
 
 
int main() {
 
    Sword mySword(10);
    Magic myMagic(15,7);
 
    mySword.Use();
    myMagic.Use();
 
    Weapon* currentWeapon = &mySword;
    currentWeapon->Use(); //정적바인딩이 돼서 부모(Weapon)에 있는 Use가 나옴
    //하지만 virtual 을 붙여주면 Sword클래스의 Use함수 호출
 
 
 
}
cs

 

객체자체를 직접넘겨주기 보단 레퍼런스나 포인터로 참조해서 넘겨주는게 좋다. 객체를 가리키고 있어야 동적바인딩이 가능하다. 

 

 

 

728x90
반응형

댓글