C++ Chapter 12.3 : override, final, 공변 반환형

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!


chapter 12. 가상 함수들 : override, final, 공변 변환형

🔔 override

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


class B : public A
{
public:
    void print(short x) { cout << "B" << endl; }
}
  • print 함수는 virtual 가상 함수이므로 자식 클래스로 하여금 오버라이딩이 될 것을 권장 하는 함수다.
  • 자식클래스 B 에서 이를 오버라이딩 했지만 현재 매개 변수 형식이 틀린 상태이다. (int != short)
    • 오버라이딩은 리턴타입 + 함수이름 + 매개변수형태 + const 유무까지 전부 똑같아야 한다.
  • 따라서 오버라이딩 되지 않았다고 판단 되어 A 클래스의 print 가 실행된다.
    • 즉, 오버라이딩 된게 아니라 오버로딩이 됨.

orverride 키워드를 붙여주면 오버라이딩 한 함수라고 강조 해주며 오버라이딩이 제대로 안됐을시 컴파일 에러를 발생시켜 준다.

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


class B : public A
{
public:
    void print(short x) override { cout << "B" << endl; }  // 에러 💥
}
  • void print(short x) override
    • 부모 클래스 중 void print(short x) 라는 함수는 없기 때문에, 즉 파라미터가 틀려서 제대로 오버라이딩 되지 않았다며 컴파일 에러를 발생시킨다.
    • void print(int x) override 라고 바꿔주면 에러가 사라질 것.
class A
{
public:
    virtual void print(int x) const { cout << "A" << endl; }
};


class B : public A
{
public:
    void print(short x) override { cout << "B" << endl; }  // 에러 💥
}
  • 뒤에 const가 안붙어서 컴파일 에러가 난다.
    • void print(short x) const override 라고 바꿔주면 에러가 사라질 것.

오버라이딩은 리턴타입 + 함수이름 + 매개변수형태 + const 유무까지 전부 똑같아야 한다.


🔔 final

final : 자식 클래스에서 오버라이딩을 못하도록 막아버린다.

class A
{
public:
    virtual void print() { cout << "A" << endl; }
};

class B : public A
{
public:
    void print() final { cout << "B" << endl; }  
}

class C : public B
{
public:
    void print()  { cout << "B" << endl; }  // 에러 💥
}
  • A 클래스의 가상함수인 print를 자식인 B 클래스에서 오버라이딩 해주었는데 final이 붙었다.
    • 오버라이딩은 이제 B 클래스에서까지가 마지막이고 그 이후 자식 클래스부터는 이 함수를 오버라이딩 할 수 없다.
    • B 클래스의 자식인 C 클래스에서 오버라이딩을 시도해서 컴파일러 에러 발생


🔔 공적 반환형(Covariant Return)

리턴 타입이 다른데도 오버라이딩이 되는 경우 👉 this (자기 자신을 가리키는 포인터)을 리턴하는 함수의 경우.

class A
{
public:
	void print() 
    { 
        cout << "A" << endl; 
    }

	virtual A* getThis() 
    { 
		cout << "A::getThis()" << endl;
		return this; 
    }
};

class B : public A
{
public:
	void print() 
    { 
        cout << "B" << endl; 
    }

	virtual B* getThis() 
    { 
		cout << "B::getThis()" << endl;
		return this; 
    }

};
  • 리턴타입이 A*, B*로 다르지만
    • B 는 A 의 자식이기에 B* 타입인 B 의 this는 A*의 성질도 가지고 있기 때문에 this를 리턴하는 함수의 경우 리턴타입이 달라도 오버라이딩이 허용된다.
      • A 와 B 가 부모 자식 관계이기 때문!
int main()
{
	A a;
	B b;

	A &ref = b;
	b.getThis()->print();   // B 출력
	ref.getThis()->print(); // A 출력

	cout << typeid(b.getThis()).name() << endl;  // class B * 출력
	cout << typeid(ref.getThis()).name() << endl; // class A * 출력

	return 0;
}
  • A &ref = b;
    • A 타입인 ref 는 B 타입의 b 객체를 참조한다.
  • b.getThis()->print();
    • b는 B 타입.
    • B 타입으로 호출하니 B 타입의 getThis() 를 호출한다.
  • ref.getThis()->print()
    • ref는 A 타입.
    • A 타입으로 호출하는데 A 타입의 getThis는 가상 함수이다.
      • 리턴 타입은 달라도 부모-자식 관계에다가 this를 리턴하므로 오버라이딩이 되어 B 타입의 getThis가 호출된다.
      • B 타입의 getThis()가 B* 타입의 포인터(b객체의 주소)를 리턴하는데 이를 호출한 `ref`가 A 타입이기 때문에 print()는 A 타입의 print()가 실행된다.


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

댓글남기기