#include <iostream>
using namespace std;

class A
{
public:
	virtual void print(int x) { cout << "A" << "\n"; }
};

class B : public A
{
public:
	void print(short x) override { cout << "B" << "\n"; } // 이 함수는 무조건 override라고 적어주는 것. 오류가 뜨면 수정할 수 있다.
    													// override 대신 final을 넣어주면 class C부터는 오버라이드가 불가능.
};

class C : public B
{
public:
	void print() { cout << "C" << "\n"; }
};

class D : public C
{
public:
	void print() { cout << "D" << "\n"; }
};

int main()
{
    A a;
    B b;
    
    A &ref = b;
    ref.print(1); // 이렇게 virtual이 있어도 파라미터가 다르면 오버로딩을 하지 않는다. 그럼 override를 추가하면 된다.
    
    return 0;
}

공변 반환형

#include <iostream>
using namespace std;

class A
{
public:
	virtual void print() { cout << "A" << "\n"; }
    virtual A* getThis() { return this; }
};

class B : public A
{
public:
	void print() override { cout << "B" << "\n"; }
    virtual B* getThis() { return this; } // 일반적으로 return type이 다르면 오버라이드가 안되지만 부모-자식 클래스기 때문에 가능하다.
};

class C : public B
{
public:
	void print() { cout << "C" << "\n"; }
};

class D : public C
{
public:
	void print() { cout << "D" << "\n"; }
};

int main()
{
    A a;
    B b;
    
    A &ref = b;
    b.getThis()->print(); // B
    ref.getThis()->print(); // A // ref의 타입이 A이기 때문에 B의 getThis에서 B의 포인터를 return 해주어도 타입을 A로 내부적으로 바꾸어 호출해									  주는 구조이다.
    						// B의 getThis -> class A 반환 -> A의 print 호출
    
    cout << typeid(b.getThis()).name() << "\n"; // class B *
    cout << typeid(ref.getThis()).name() << "\n"; // class A *
    
    return 0;
}