본문 바로가기

C++

C++ 가상 함수(Virtual),순수 가상 함수 , 가상 소멸자

가상 함수는 기본 클래스(상속되지 않은 클래스) 내에서 선언되어 파생 클래스에 의해 재정의되는 맴버 함수입니다.

 

1. 클래스의 공개(public) 섹션에 선언합니다.

2. 가상 함수는 정적(static)일 수 없으며 다른 클래스의 친구(friend) 함수가 될 수도 없습니다.

3. 가상 함수는 실행시간 다형성을 얻기위해 기본 클래스의 포인터 또는 참조를 통해 접근(access)해야 합니다.

4. 가상 함수의 프로토타입(반환형과 매개변수)은 기본 클래스와 파생 클래스에서 동일합니다.

5. 클래스는 가상 소멸자를 가질 수 있지만 가상 생성자를 가질 수 없습니다.


가상함수는 실행시간(Runtime)에 함수의 다형성(Polymorphism)을 구현하는데 사용됩니다

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
class WeaponManage
{
public:
    virtual void WeaponName() {
        cout << "NULL" << endl;
    }
};
//칼
class  Knife :public WeaponManage
{
public:
    void WeaponName()
    {
        cout << "칼" << endl;
    }
};
//활
class  Bow :public WeaponManage
{
public:
    void WeaponName()
    {
        cout << "활" << endl;
    }
};
// 도끼
class  Axe : public WeaponManage
{
public:
    void WeaponName()
    {
        cout << "도끼" << endl;
    }
};
int main()
{
    WeaponManage * weapon;
    Axe axe;
    Bow bow;
    Knife knife;
    weapon = &axe;
    weapon->WeaponName();
    weapon = &bow;
    weapon->WeaponName();
    weapon = &knife;
    weapon->WeaponName();
}
 
cs

 

순수 가상함수

순수 가상 함수란 순수 가상 함수와 동일하지만 가상 함수는 정의를 하지 않을 때의 경우가 정의되어 있지만
순수 가상 함수는 완벽하게 비어있는 함수를 의미한다.
그렇기 때문에 순수 가상 함수는 무조건적으로 재정의를 해주어야 하는데 재정의를 해주지 않을 경우에는 에러를
발생하게 된다.

순수 가상함수의 정의방법은 일반 가상함수뒤에 = 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
class WeaponManage
{
public:
    virtual void WeaponName() = 0;
 
};
 
//칼
class  Knife :public WeaponManage
{
public:
    void WeaponName() override
    {
        cout << "칼" << endl;
    }
 
};
 
int main()
{
    WeaponManage* weapon = new Knife;
    
    weapon->WeaponName();
}
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
36
37
38
class WeaponManage
{
public:
    virtual void WeaponName() {
        cout << "NULL" << endl;
    }
 
    virtual ~WeaponManage()
    {
        cout << "무기 삭제" << endl;
    }
};
 
//칼
class  Knife :public WeaponManage
{
public:
    void WeaponName()const
    {
        cout << "칼" << endl;
    }
 
    ~Knife()
    {
        cout << "칼" << endl;
    }
};
 
 
int main()
{
    WeaponManage* weapon = new WeaponManage;
 
    Knife* knife = new Knife;
 
    weapon = knife;
    delete(weapon);
}
cs

 

분명 나는 weapon만 삭제하였으나 칼도 삭제가 되었다.
이유로는 다형성을 하기 위해서 weapon 부모 클래스의 포인터로부터 Knife 자식 클래스를 호출하면
가상 함수로 정의되지 않은 자식 클래스의 오버라이딩 된 함수를 호출하여 부모 클래스의 멤버 함수가 호출된다.
소멸자 또한 자식 클래스에서 오버라이딩 된 함수라고 볼 수 있기 때문에 부모 포인터로 객체를 삭제하면 부모 클래스의 소멸자가 호출된다.

따라서 소멸자를 가상 함수로 선언하지 않으면 자식 클래스의 소멸 자는 결코 호출되지 않는다. 그렇기 때문에
weapon이 knife의 주소를 가지고 knife의 소멸자를 호출하였으나 부모 클래스의 멤버 함수가 호출돼서 무기 삭제만 출력되는 것이다.
하지만 가상 함수 키워드를 사용하면 자식 클래스에서 재정의될 수 있음을 명시하기 때문에 포인터의 종류에 상관없이 항상 자식 클래스의 메서드가 호출된다.
위에 예제에서는 부모 클래스의 소멸자가 가상 함수이기 때문에 자식 클래스가 재정의 될 수 있다고 판단하여 자식 클래스의 소멸자를 호출한 후 부모 클래스의 소멸자를 호출한 후 종료된다.

'C++' 카테고리의 다른 글

C++ 구조체(struct)  (0) 2020.07.20
Inline함수  (0) 2020.01.19
C++의 Casting,형변환  (0) 2020.01.19
STL vector에서 push_back과 emplace_back의 차이점  (0) 2019.12.04
Precompiled header(미리컴파일 헤더)  (0) 2019.09.04