C , C++, C#

C++ 가상함수 이해정리

vhxpffltm 2019. 3. 6. 22:31

참고 : https://koocci.github.io


가상함수를 하기 전, 다음을 확인하자.


int main(){

    Base* bptr = new Derived(); //컴파일 ok

    Derived* dptr = bptr; //컴파일 에러

    ...

    Derived* dptr = new Derived(); // 컴파일 ok

    Base* bptr = dptr; // 컴파일 ok

}

// Base는 부모 클래스, Derived는 자식 클래스

Derived 클래스는 Base 클래스의 유도 클래스이므로 Base 클래스의 포인터 변수로 Derived 객체의 참조가 가능하고 문제 없이 컴파일이 가능하다. 

그러나 bptr은 Base형 포인터이므로 bptr이 가리키는 대상은 Base 객체일 수도 있는 것이다. 그럴 경우, Derived* dptr = bptr; 은 성립되지 않으므로 컴파일 에러를 발생시킨다.


*c++ 컴파일러는 포인터 연산의 가능성 여부를 포인터의 자료형을 기준으로 판단**


앞에서 한 다형성을 복습한 것이다. 코드를 보고 다시한번 복습해보자.

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
#include<iostream>
#include<string.h>
#include<string>
 
using namespace std;
 
class First {
public:
    void FirstFunc() const { cout << "FirstFunc" << endl; }
};
 
class Second : public First {
public:
    void SecondFunc() const { cout << "SecondFunc" << endl; }
};
 
class Third : public Second {
public:
    void ThirdFunc() const { cout << "ThirdFunc" << endl; }
};
 
int main() {
    Third* tptr = new Third();
    Second* sptr = tptr;
    First* fptr = sptr;
 
 
    tptr->FirstFunc();
    tptr->SecondFunc();
    tptr->ThirdFunc();
 
    //tptr은 Third포인터 형으로 해당 클래스 및 상속받은 클래스에 대해 접근가능
 
    sptr->FirstFunc();
    sptr->SecondFunc();
    //sptr->ThirdFunc();
 
    //sptr이 가리키는 객체는 frist클래스 직,간접 상속하는 객체
 
    fptr->FirstFunc();
    //tptr->SecondFunc();
    //tptr->ThirdFunc();
    //주석을 지우면 컴파일 에러.
    
    return 0;
    // 포인터 연산의 가능성여부를 포인터의 자료형을 기준으로 판단
    // 포인터형에 해당하는 클래스에 정의된 맴버에만 접근가능 **
}
cs




가상함수 : 실제 존재하지 않는것을 존재하는 것처럼 느끼게함 -> 코드에 보이지만 실행때는 없는것처럼

가상함수로 선언하면 해당 함수호출 시, 포인터의 자료형을 기반으로 호출대상을 결정하지 않고, 

포인터 변수가 실제로 가리키는 객체를 참조하여 호출의 대상을 결정 


다음의 코드를 확인하고 직접 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
#include<iostream>
#include<string.h>
#include<string>
 
using namespace std;
 
class First {
public:
     void MyFunc() const { cout << "FirstFunc" << endl; }
};
 
class Second : public First {
public:
     void MyFunc() const { cout << "SecondFunc" << endl; }
};
 
class Third : public Second {
public:
     void MyFunc() const { cout << "ThirdFunc" << endl; }
};
 
int main() {
    Third* tptr = new Third();
    Second* sptr = tptr;
    First* fptr = sptr;
 
    fptr->MyFunc();
    sptr->MyFunc();
    tptr->MyFunc();
    // 이 문장들은 해당 포인터가 무슨 형인지 알고 있음
    // 함수 오버라이딩 관계로 존재함을 안다. 
    delete tptr;
    return 0;
 
    // virtual이 없다면 각 클래스의 메서드를 실행한다. 하지만 이 함수들은 
    // 오버라이딩 되어있고 포인터의 자료형에 따라 호출되는 함수의 종류가 달라짐을 막기위해 virtual키워드를 사용
    // virtual을 사용하여 포인터의 자료형을 기반으로 호출대상을 정하지 않고
    // 포인터 변수가 실제로 가리키는 객체를 참조하여 호출의 대상을 결정
}
cs


First클래스 메서드를 void virtual MyFunc() 으로 고치면 출력결과가 바뀜을 알 수 있다. virtual은 없는것처럼 치라는 뜻.

해당 함수가 없는것처럼 실행되기 때문에 메서드를 오버라이딩 하고 있는 클래스의 함수가 대신 호출이 일어난다.

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

[C/C++] 이동 생성자  (0) 2019.11.03
[C++] 가상함수 테이블  (0) 2019.03.12
C++ 함수 오버라이딩 간단정리  (0) 2019.03.06
C++ 다형성,상속 간단정리  (0) 2019.02.26
[자료구조] 링크드 리스트(C++)  (0) 2019.02.20