Project Hub

11. virtual 함수와 다형성 본문

C++/c++ basic

11. virtual 함수와 다형성

safy 2022. 12. 21. 13:58
728x90
반응형

이전 글

2022.12.21 - [C++/c++ basic] - 10. 업 캐스팅 & 다운 캐스팅

 

10. 업 캐스팅 & 다운 캐스팅

이전 글 2022.12.21 - [C++/c++ basic] - 9. 전위/후위 증감 연산자 오버로딩 & 첨자 연산자 오버로딩 9. 전위/후위 증감 연산자 오버로딩 & 첨자 연산자 오버로딩 이전 글 2022.12.21 - [C++/c++ basic] - 8. 입출력

projecthub.tistory.com


다형성

하나의 메소드를 호출했음에도 불구하고 여러가지 다른 작업들을 하는 것.

virtual 키워드

컴파일 시에 어떤 함수가 실행될 지 정해지지 않고 런타임 시 정해지는 동적 바인딩을 수행한다.

 

예제 코드는 다음과 같다.

#include <iostream>
 
class CBase
{
public:
    CBase() { std::cout << "=== 기반 클래스 ===" << std::endl; }
     
    virtual void what() { std::cout << "기반 클래스의 what()" << std::endl; }
};
 
class CDerived : public CBase
{
public:
    CDerived() { std::cout << "=== 파생 클래스 ===" << std::endl; }
 
    void what() { std::cout << "파생 클래스의 what()" << std::endl; }
};
 
int main()
{
    CBase b;
    CDerived d;
 
    CBase* pb = &d;
    CBase* pc = &b;
 
    std::cout << "=== 실제 객체는 CBase ===" << std::endl;
    pc->what();
 
    std::cout << "=== 실제 객체는 CDerived ===" << std::endl;
    pb->what();
 
    return 0;
}
 
// 출력
=== 기반 클래스 ===
=== 기반 클래스 ===
=== 파생 클래스 ===
=== 실제 객체는 CBase ===
기반 클래스의 what()
=== 실제 객체는 CDerived ===
파생 클래스의 what()


override 키워드

파생 클래스에서 기반 클래스의 가상 함수를 오버라이드 하는 경우, override 키워드를 통해 명시적으로 표시 가능.


아래의 경우, override 가 정상적으로 되지 않는다. 

 

이유는, const 키워드로 상수 함수로 선언되었기 때문이다.

#include <iostream>
 
class CBase
{
public:
    CBase() { std::cout << "=== 기반 클래스 ===" << std::endl; }
     
    virtual void what() { std::cout << "기반 클래스의 what()" << std::endl; }
};
 
class CDerived : public CBase
{
public:
    CDerived() { std::cout << "=== 파생 클래스 ===" << std::endl; }
 
    void what() const { std::cout << "파생 클래스의 what()" << std::endl; } // const 로 선언됨.
};
 
int main()
{
    CBase b;
    CDerived d;
 
    CBase* pb = &d;
    CBase* pc = &b;
 
    std::cout << "=== 실제 객체는 CBase ===" << std::endl;
    pc->what();
 
    std::cout << "=== 실제 객체는 CDerived ===" << std::endl;
    pb->what();
 
    return 0;
}



위의 문제를 해결하기 위해서는 const 키워드를 지워주면 된다. 

또한 override 키워드를 입력하여 명시적으로 표시하는 것도 방법이다.

class CBase
{
public:
    CBase() { std::cout << "=== 기반 클래스 ===" << std::endl; }
     
    virtual void what() { std::cout << "기반 클래스의 what()" << std::endl; }
};
 
class CDerived : public CBase
{
public:
    CDerived() { std::cout << "=== 파생 클래스 ===" << std::endl; }
 
    void what() override { std::cout << "파생 클래스의 what()" << std::endl; }
};
...
...


순수 가상 함수

반드시 오버라이딩 되어야만 하는 함수.

 

가상 함수에 = 0; 을 붙여서 만든다.

 

순수 가상 함수를 최소 한개 포함하고 있는, 반드시 상속 되어야 하는 클래스를 추상 클래스 라고 한다.

#include <iostream>
 
class CAnimal
{
public:
    CAnimal() {}
    virtual ~CAnimal() {}
    virtual void speak() = 0; //  순수 가상 함수
};
 
class CDog : public CAnimal
{
public:
    CDog() : CAnimal() {}
    virtual ~CDog() {}
    virtual void speak() override { std::cout << "왈왈" << std::endl; }
};
 
class CCat : public CAnimal
{
public:
    CCat() : CAnimal() {}
    virtual ~CCat() {}
    virtual void speak() override { std::cout << "야옹야옹" << std::endl; }
};
 
int main()
{
    // CAnimal animal; 객체 생성은 불가하다. 순수 가상 함수가 있기 때문에 막았다.  
    CAnimal* dog = new CDog();
    CAnimal* cat = new CCat();
 
    dog->speak();
    cat->speak();   return 0;
}


다중상속 시 주의해야 할 점

아래와 같이 상속이 이루어진다면,

 

B와 C 클래스가 A 클래스의 변수를 상속 받기 때문에 D 클래스에서는 A의 모든 내용이 중복되는 문제가 발생한다.

class A
{
public:
    // ...
}
class B : public A
{
public:
    // ...
}
class C : public A
{
public:
    // ...
}
class D : public B, public C
{
public:
    // ...
}


이런 중복의 문제를 해결하기 위해서는 virtual 키워드를 사용하면 된다.

class A
{
public:
    // ...
}
class B : public virtual A
{
public:
    // ...
}
class C : public virtual A
{
public:
    // ...
}
class D : public B, public C
{
public:
    // ...
}
728x90
반응형
Comments