본문 바로가기

공부/C++

C++ 객체지향 프로그래밍 Chapter 23 다형성과 가상함수

가상함수가 쓰이는 시기는 언제인가. ???


Shape 클래스와 자식 클래스들.

책 페이지 667 의 코드[단일 실행이 가능함] 

책 내의 코드 우측에 쓰여있는 문장이나 굵은 글씨로 된 것 들을 모아두었다.

이 코드는 가상함수가 쓰인것이 아니다.




클래스가 3개나 있어서 코드가 꽤나 길지만 뭐 복잡한 부분은 없고 3개의 클래스가 있고

Shape 클래스에 2개의 도형이 들어간다.

멤버 변수의 경우에는 부모 클래스로부터 상속 받은 것을 그대로 사용할 수 밖에 없지만 멤버 함수의 경우에는 부모 클래스에서 구현한 것이 맘에 들지 않는다면 자기에게 맞도록 새롭게 정의할 수 있다.

Draw()함수가 바로 그런 예 이다.


보관한 객체를 사용하는것과 그에 따른 문제점.


예제 코드


위의 코드에서 int main() 쪽만 내용이 다른 코드이다.
int main()
{

// 도형들을 담을 배열을 준비한다
Shape* shapes[5] = {NULL}; // Shape* 타입의 배열을 준비한다.[포인터 배열]

// 각 타입의 객체를 생성해서 배열에 보관한다.
shapes[0] = new Circle( 100, 100, 50);
shapes[1] = new Rectangle( 300, 300, 100, 100);
shapes[2] = new Rectangle( 200, 100, 50, 150);
shapes[3] = new Circle(100, 300, 150);
shapes[4] = new Rectangle( 200, 200, 200, 200); //여러가지 타입의 객체를 동적으로 생성한
                                                                            //뒤에 반환되는 포인터를 배열에 보관한다.

// 배열의 보관된 모든 객체를  그린다.
for (int i = 0; i < 5; ++i)
shapes[i]->Draw();          //반복문을 사용하여 모든 원소에 대한 Draw() 멤버함수를 호출한다.

// 배열의 보관된 모든 객체를 소멸시킨다.
for (int i = 0; i < 5; ++i)
{
delete shapes[i];
shapes[i] = NULL;     //반복문을 통해 값을 삭제 하고 NULL로 채워준다.
}

system("pause");        //디버깅시에 그냥 출력창이 꺼지는것을 방지하기 위한 코드.

return 0;
}



출력화면




for (int i = 0; i < 5; ++i)
shapes[i]->Draw();          //반복문을 사용하여 모든 원소에 대한 Draw() 멤버함수를 호출한다.

라는 2줄의 코드 만으로 배열에 저장된 모든 객체에 대해서 Draw함수를 호출하고 있다. 이것이 상속의 장점이다.

하지만 만약 도형 클래스들이 수십개 있고 또 클래스마다 배열을 따로 만들어서 관리했다면 Draw함수를 호출하는 코드도 많아지게 될 것이다.


다음 코드를 보자.[프로그램 코드에 넣는게 아니고 그냥 보기만 하자]


이 소스코드를 보면 상속을 사용하는 것이 얼마나 편한지를 알 수 있다. 하지만 위의 출력화면 을 보면 이 예제에 문제가 있다는 것을 알 수 있다.

모든 줄의 출력이 [Shape]라는 문자열로 시작하는것이 문제인데. 이는 Shape::Draw() 함수를 호출했다는 뜻이다.

즉 이 위의 코드나 바로 위의 출력화면이 나오는 코드나 둘 다 잘못되었다는 것이다.

바로 지금!  이러한 문제가 발생할 때에 가상함수가 필요하다.




허탈하고 간단하게도 코드를 약간만 수정하여 가상함수를 사용하면 쉽게 이 문제를 풀 수 있다.


class Shape
{ //Shape 클래스의 정의, 원 , 사각형 같은 일반적인 도형들이 
public: //공통적으로 가지는 속성들을 여기에 정의한다. 자식 클래스들은 
void Move(double x, double y); //이 멤버들을 자동적으로 상속받게 된다.//
void Draw() const; //모든 도형 클래스들은 자신을 움직이거나, 그리는 함수가 필요하다.


이 코드에서 

void Draw() const; //모든 도형 클래스들은 자신을 움직이거나, 그리는 함수가 필요하다. 
를 
virtual void Draw() const;


로 바꾼뒤에 출력을 해보면 다음과 같은 출력화면이 나올 것이다.



Shape::Draw 함수를 가상함수로 만들면 Circle::Draw와 Rectangle::Draw 함수 역시 자동적으로 가상함수가 된다.

또한 cirtual 키워드는 클래스의 정의 안쪽에서만 한 번 붙여주면 된다. 클래스 밖에서 함수를 정의할 때는 virtual 키워드가 필요 없다.

최종 출력화면을 나타내는 코드 파일 업로드.





**공부에 도움이 되었다면 손가락 버튼을 클릭해주심 좋겠습니다.**