C++ 다형성


다형성은 말 그대로 여러 형태를 의미합니다. 다형성은 클래스 사이에 계층 구조가 있고 클래스가 상속을 통해 관련될 때 사용됩니다.

C++ 다형성은 멤버 함수가 호출될 때 함수를 호출하는 개체의 유형에 따라 다른 함수가 실행된다는 것을 의미합니다.

아래 예에서 기본 클래스 Shape는 다음과 같이 두 개의 클래스로 파생됩니다.

#include <iostream> 
using namespace std;
 
class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      int area()
      {
         cout << "Parent class area :" <<endl;
         return 0;
      }
};
class Rectangle: public Shape{
   public:
      Rectangle( int a=0, int b=0):Shape(a, b) { }
      int area ()
      { 
         cout << "Rectangle class area :" <<endl;
         return (width * height); 
      }
};
class Triangle: public Shape{
   public:
      Triangle( int a=0, int b=0):Shape(a, b) { }
      int area ()
      { 
         cout << "Triangle class area :" <<endl;
         return (width * height / 2); 
      }
};
// 程序的主函数
int main( )
{
   Shape *shape;
   Rectangle rec(10,7);
   Triangle  tri(10,5);

   // 存储矩形的地址
   shape = &rec;
   // 调用矩形的求面积函数 area
   shape->area();

   // 存储三角形的地址
   shape = &tri;
   // 调用三角形的求面积函数 area
   shape->area();
   
   return 0;
}

위 코드를 컴파일하고 실행하면 다음과 같은 결과가 생성됩니다.

Parent class area
Parent class area

오류 출력의 이유는 Calling입니다. function Area()는 컴파일러에 의해 기본 클래스의 버전으로 설정됩니다. 이를 정적 다형성 또는 정적 연결이라고 합니다. 함수 호출은 프로그램이 실행되기 전에 준비됩니다. 프로그램 컴파일 중에 Area() 함수가 이미 설정되어 있기 때문에 이를 조기 바인딩이라고도 합니다.

하지만 이제 프로그램을 약간 수정해 보겠습니다. Shape 클래스에서 다음과 같이 virtual 키워드를 Area() 선언 앞에 배치합니다.

class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      virtual int area()
      {
         cout << "Parent class area :" <<endl;
         return 0;
      }
};

수정 후 이전 예제 코드를 컴파일하고 실행하면 , 다음 결과를 생성합니다.

Rectangle class area
Triangle class area

이 시점에서 컴파일러는 포인터의 유형이 아닌 내용을 보고 있습니다. 따라서 tri, rec 클래스의 객체 주소가 *shape로 저장되므로 해당 Area() 함수가 호출된다.

보시다시피 각 하위 클래스에는 기능 영역()이 독립적으로 구현되어 있습니다. 이것이 다형성이 일반적으로 사용되는 방식입니다. 다형성을 사용하면 이름은 같지만 구현이 다른 함수를 포함하는 여러 가지 클래스를 가질 수 있으며 함수의 매개변수도 동일할 수 있습니다.

가상 함수

가상 함수virtual 키워드를 사용하여 기본 클래스에 선언된 함수입니다. 파생 클래스의 기본 클래스에 정의된 가상 함수를 재정의하는 경우 컴파일러에 해당 함수에 정적으로 연결하지 않도록 지시합니다.

우리가 원하는 것은 프로그램의 어느 시점에서든 호출되는 객체의 유형에 따라 호출할 함수를 선택할 수 있다는 것입니다. 이 작업을 동적 연결 또는 후기 바인딩이라고 합니다.

순수 가상 함수

파생 클래스에서 재정의하는 것이 객체에 더 잘 작동하도록 기본 클래스에서 가상 함수를 정의하고 싶지만 기본 클래스의 가상 함수를 이해하지 못할 수도 있습니다. , 이때 순수 가상 함수가 사용됩니다.

기본 클래스의 가상 함수 Area()를 다음과 같이 다시 작성할 수 있습니다.

class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      // pure virtual function
      virtual int area() = 0;
};

= 0은 컴파일러에게 함수에 본문이 없음을 알리고 위의 가상 함수는 순수 가상 함수입니다.