본문 바로가기
스터디/C++

[ C++ ] 가상 함수( virtual Function ) , 순수 가상함수( Pure Virtual Function ), 추상 클래스( Abstract Class )

by 알 수 없는 사용자 2019. 10. 10.

( * 개발환경을 Dev-C++-> Visual Studio로 변경함. 헤더 파일과 클래스를 관리하기 편하고 그 외 코딩하기에도 편함. )

 

가상 함수에 들어가기 전에, 다음의 예제를 살펴보고 사용하는 이유를 알아보자.

 

클래스 A

A를 상속하는 클래스 B,

B를 상속하는 클래스 C가 있다.

 

그리고 Show 함수는 오버라이딩으로 구현되어있다.

 

메인 함수를 살펴보면 

34행) C형 포인터 변수 c는 C 객체를 가리키고 있고,

35행) B형 포인터 변수 b는 C객체를 가리키고 있고, ( 결론적으론 .. c가 객체 C를 가리키니까. )

36행) A형 포인터 변수 a는 C 객체를 가리키고 있다. ( 결론적으론.. b가 객체 C를 가리키니까. )

 

이 말 즉은, 

"자료형이 기초 클래스인 포인터 변수는 유도 클래스의 객체를 가리킬 수 있다"는 것을 알 수 있다.

 

하지만, 실행 결과를 보면 

포인터 변수 a는 "A의 Show 호출"을 출력하고

포인터 변수 b는 "B의 Show 호출"을 출력하고

포인터 변수 c는 "C의 Show 호출"을 출력하는 것을 확인할 수 있다.

 

왜일까?

 

이유는 C++의 컴파일러의 판단 기준 때문인데, 

 

C++ 컴파일러는 포인터를 이용한 연산의 가능성 여부를 판단할 때, 포인터의 자료형을 기준으로 판단하지, 실제 가리키는 객체의 자료형을 기준으로 판단하지 않기 때문이다.

 

그래서 포인터 형에 해당하는 클래스에 정의된 멤버에만 접근이 가능한 것이었다.

a는 포인터 형이 A이기에 A의 Show를 호출하고

b는 포인터 형이 B이기에 B의 Show를 호출하고

c는 포인터 형이 C이기에 C의 Show를 호출한 결과가 나온 것이다.

 

여기서 이제 가상 함수( virtual Function )를 사용하는 이유가 생긴다. 

 

"하지만, 함수 오버 라이딩을 했다는 것은 해당 객체에서 호출되어야 하는 함수를 바꾼다는 의미인데, 포인터 변수의 자료형에 따라서 호출되는 함수의 종류가 달라지는 것은 문제가 될 수 있다."

 

그래서 등장한 것이 가상 함수이다.

가상 함수를 사용하면 해당 객체에서 함수가 호출이 된다.

 

가상 함수의 선언은 간단하다. 그냥 virtual 키워드의 선언을 하면 된다. ( 기초 클래스의 오버 라이딩될 함수에 선언한다. )

그러면 유도 클래스의 오버 라이딩하는 함수들도 자동으로 가상 함수가 된다.

 

이렇게 선언한다. 그러면 실행 결과를 보자.

 

 

이렇게 된다. 즉, 가리키는 객체의 함수를 호출하게 된다.

 

이해를 돕기 위해 객체를 각 포인터형 클래스로 바꿔서 해보겠다.

 

짠! 이렇게 출력 결과가 나온다.

 

근데 virtual를 기초 클래스에만 작성해도 결과는 같지만,

글쓴이 생각은 가독성을 높이기 위해 유도 클래스에도 가상 함수를 선언하는 편이 나을 듯하다.

( 그럼 현업에서도 코드 해석에 용이할 것이다. )

 

그럼 순수 가상 함수( Pure Virtual Function )는 무엇일까?

 

예제를 보자.

 

 

Animal 클래스를 상속하는 Turtle 클래스와 Rabbit 클래스가 있다.

그리고 둘 다 Run함수를 오버 라이딩하고 있는 상황이다.

 

이렇게

Animal 클래스는 동물 모두 Run함수를 가지고 있기에 정의한 것이지, Animal을 객체 생성 목적을 가지고 있지 않는다.

( * 여기서 포인터 형과 헷갈리지 말자.

Animal * rabbit = new rabbit() 

포인터 형이 Animal이어도 가리키는 객체는 rabbit으로 생성한다. )

 

 

하지만 우리는 사람이기에 클래스 Animal의 객체 생성을 해버리는 경우가 생길 수 있다.

 

이런 원치 않는 상황을 막기 위해 존재하는 것이 순수 가상 함수( Pure Virtual Function )이다.

순수 가상 함수는 즉, 함수의 몸체가 정의되지 않는 함수를 의미한다.

순수 가상함수는 이렇게 선언하면 된다. 

이것은 0의 대입을 의미하는 것이 아니라 명시적으로 몸체를 정의하지 않았음을 컴파일러에게 알리는 것이다.

 

그래서 Animal 객체를 생성하면 컴파일 에러가 발생하면서 실수를 방지할 수 있으며,

Animal 클래스를 보고 유도 클래스에서 재정의 함을 명시적으로 알 수 있기도 하다.

 

( 컴파일 에러 발생. )

 

여기서 추상 클래스( Abstract Class )가 보이는 데,

 

이렇듯 하나 이상의 멤버 함수를 순수 가상 함수로 선언한 클래스를 추상 클래스라고 한다.

즉, 완전하지 않아서 객체 생성이 불가능 한 클래스라는 의미를 가진다.