개발자 '쑥말고인절미'
가상 함수 본문
객체 포인터 부분을 보고있었는데 소스를 바꿔보고 예상한 결과가 나오지 않아서 공부한 내용이다.
#include <iostream>
using namespace std;
class Person{
public:
void Sleep() { cout<<"Sleep of Person"<<endl;}
void Study() { cout<<"Study of Person"<<endl;}
void Work() { cout<<"Work of Person"<<endl;}
};
class Student : public Person{
public:
void Sleep() { cout<<"Sleep of Student"<<endl;}
void Study() { cout<<"Study of Student"<<endl;}
void Work() { cout<<"Work of Student"<<endl;}
};
class PartTimeStudent : public Student{
public:
void Sleep() { cout<<"Sleep of PartTimeStudent"<<endl;}
void Study() { cout<<"Study of PartTimeStudent"<<endl;}
void Work() { cout<<"Work of PartTimeStudent"<<endl;}
};
int main()
{
Person *ptr1 = new Student();
Person *ptr2 = new PartTimeStudent();
Student *ptr3 = new PartTimeStudent();
ptr1->Sleep();
ptr2->Sleep();
ptr3->Study();
delete ptr1;
delete ptr2;
delete ptr3;
return 0;
}
| 예상한 출력 결과 | 실제 출력 결과 |
![]() |
![]() |
위 소스를 보면서 예상한 출력 결과와 실제 결과는 달랐다. 왜 저렇게 출력이 됐는지 전혀 감이 안잡혀서 책임님께 여쭤보고나서야 알게 됐는데 내가 예상한 출력결과는 가상함수가 있어야 가능했던 것이다.
내 생각
- 메인부분을 보면 ptr1은 Student로 객체가 생성됐고, ptr2는 PartTimeStudent로 객체가 생성됐고, ptr3도 PartTimeStudent로 객체가 생성이 되었다. 그러니 ptr1의 Sleep메소드를 호출하면 Student객체의 Sleep으로 가고, ptr2의 Sleep메소드는 PartTimeStudent객체의 Sleep으로 가고, ptr3의 Study메소드는 PartTimeStudent객체의 Study로 가야한다.
하지만 실제 결과는 ptr1과 ptr2는 Person객체의 Sleep메소드, ptr3는 Student객체의 Study메소드가 호출됐다. 왜냐하면 ptr1과 ptr2는 Person객체로 생성이 된 것이고, ptr3는 Student객체로 생성이 되었기 때문이다. 나는 new Student(), new PartTimeStudent()만 보고 생각을 했던 것이다.
내가 원하는 결과값을 얻기위해서는 가상함수를 이용해야 하는데 아래처럼 virtual만 추가해주면 된다.
#include <iostream>
using namespace std;
class Person{
public:
virtual void Sleep() { cout<<"Sleep of Person"<<endl;} //변경된 부분
virtual void Study() { cout<<"Study of Person"<<endl;} //변경된 부분
virtual void Work() { cout<<"Work of Person"<<endl;} //변경된 부분
};
class Student : public Person{
public:
void Sleep() { cout<<"Sleep of Student"<<endl;}
void Study() { cout<<"Study of Student"<<endl;}
void Work() { cout<<"Work of Student"<<endl;}
};
class PartTimeStudent : public Student{
public:
void Sleep() { cout<<"Sleep of PartTimeStudent"<<endl;}
void Study() { cout<<"Study of PartTimeStudent"<<endl;}
void Work() { cout<<"Work of PartTimeStudent"<<endl;}
};
int main()
{
Person *ptr1 = new Student();
Person *ptr2 = new PartTimeStudent();
Student *ptr3 = new PartTimeStudent();
ptr1->Sleep();
ptr2->Sleep();
ptr3->Study();
delete ptr1;
delete ptr2;
delete ptr3;
return 0;
}
virtual을 내가 이해한대로 적자면 ptr1->Sleep() 했을 때 우선 ptr1의 객체인 Person클래스로 간다. 거기서 Sleep메소드를 찾았는데 virtual이 적혀 있다면 new Student로 생성한 Student클래스로 가서 Sleep메소드가 있는지 확인한다. 만약 있다면 Student클래스에 있는 Sleep메소드가 호출되고, 없다면 Person클래스의 Sleep메소드가 호출된다. 위 소스에서는 Student클래스에 Sleep메소드가 있기 때문에 Person클래스의 Sleep메소드가 아닌 Student클래스의 Sleep메소드가 호출된다. 이와 같은 방식으로 ptr2와 ptr3도 동작하게 된다.
위 소스를 사용하면 내가 예상한 결과대로 출력된다.
가상함수가 C++에서 매우 중요하다고 하는데도 아직은 왜 쓰는지 이해가 잘 안갔지만 이젠 대강 알 것 같다.
위에 내용을 다 이해했다면 아래 소스의 출력결과가 왜 저렇게 나오는지 단박에 이해할테니 설명은 적지 않겠다.
#include <iostream>
using namespace std;
class Person{
public:
virtual void Sleep() { cout<<"Sleep of Person"<<endl;} //변경된 내용
virtual void Study() { cout<<"Study of Person"<<endl;} //변경된 내용
void Work() { cout<<"Work of Person"<<endl;}
};
class Student : public Person{
public:
void Sleep() { cout<<"Sleep of Student"<<endl;}
void Study() { cout<<"Study of Student"<<endl;}
void Work() { cout<<"Work of Student"<<endl;}
};
class PartTimeStudent : public Student{
public:
void Sleep() { cout<<"Sleep of PartTimeStudent"<<endl;}
void Study() { cout<<"Study of PartTimeStudent"<<endl;}
void Work() { cout<<"Work of PartTimeStudent"<<endl;}
};
int main()
{
Person *ptr1 = new Student();
Person *ptr2 = new PartTimeStudent();
Student *ptr3 = new PartTimeStudent();
Person *ptr4 = new Student(); //추가된 내용
ptr1->Sleep();
ptr2->Sleep();
ptr3->Study();
ptr4->Work(); //추가된 내용
delete ptr1;
delete ptr2;
delete ptr3;
delete ptr4; //추가된 내용
return 0;
}

+22.04.07
가상함수라는 것은 C++의 개념이 아닌 객체지향의 개념이라고 한다. 그래서 C++뿐만 아니라 JAVA, C#과 같은 객체지향 언어에서도 적용하는 방법에 약간의 차이가 있을 뿐 이와 동일한 개념의 문법이 제공되고 있다고 한다.
가상함수의 선언은 virtual 키워드의 선언을 통해서 이뤄진다. 그리고 이렇게 가상함수가 선언되고 나면, 이 함수를 오버라이딩 하는 함수도 가상함수가 된다.
함수가 가상함수로 선언되면, 해당 함수 호출 시 포인터의 자료형을 기반으로 호출대상을 결정하지 않고, 포인터 변수가 실제로 가리키는 객체를 참조하여 호출의 대상을 결정한다.(아래 그림 참고)

위의 그림에서 B객체는 이름이 존재하지 않기 때문에 포인터변수로 선언한 abc가 아니면 사용할 수 없다.
가상함수 테이블에 의한 속도의 저하 : 클래스에 가상함수가 포함되면, 가상함수 테이블이 생성되고, 또 이 테이블을 참조하여 호출될 함수가 결정되기 때문에 실행속도가 감소하기 마련이다. 하지만 그 속도의 차이는 극히 미미하고 또 이러한 단점에도 불구하고 가상함수는 많은 장점을 제공하기 때문에 유용하게 활용되는 것이다.
'STUDY > C++ & MFC' 카테고리의 다른 글
| [에러] 비정적 멤버 참조는 특정 개체에 상대적이어야 합니다 (0) | 2022.04.11 |
|---|---|
| 계산 mfc 구현 참고링크 (1) | 2022.04.08 |
| 참조자와 포인터 (0) | 2022.04.06 |
| 문제 04-2 [다양한 클래스의 정의] (0) | 2022.04.04 |
| 댓글로 메모 (28) | 2022.04.04 |

