모두의 코드 내용을 공부하고 정리한 내용입니다.



가상 함수

  • 가상함수는 파생 클래스에서 재정의할 것으로 기대하는 멤버 함수를 의미
    • 이로써 자식 클래스에서 오버라이딩된 함수가 상황에 맞게 호출이 될 수 있다.
      • 포인터 또는 기본 클래스에 대한 참조를 이용하여 자식 클래스의 객체를 참조할 때
      • 해당 객체에 대해 가상함수를 이용하여 자식 클래스의 멤버함수를 분명하게 사용하려는 목적(아래 예시)

가상함수 없다면?

class A
{
public:
	void print() { cout << "A" << endl; }
};
class B : public A
{
public:
	void print() { cout << "B" << endl; }
};
class C : public B
{
public:
	void print() { cout << "C" << endl; }
};
int main()
{
	A a;
	B b;
	C c;

    A &ref1 = b;
    ref1.print();  // "A" 출력

	B &ref2 = c;
	ref2.print();  // "B" 출력
}
  • 위의 ref1, ref2는 각각 A타입, B타입이기에 b와 c를 가르켜도 A의 print함수, B의 print함수가 호출된다.

가상함수 사용한다면?

class A
{
public:
	virtual void print() { cout << "A" << endl; }
};
class B : public A
{
public:
	void print() { cout << "B" << endl; }
};
class C : public B
{
public:
	void print() { cout << "C" << endl; }
};
int main()
{
	A a;
	B b;
	C c;

    A &ref1 = b;
    ref1.print();  // "B" 출력

	B &ref2 = c;
	ref2.print();  // "C" 출력
}
  • 가상 함수로 지정된 함수를 호출시 참조하는 객체의 타입이 무엇인가에 따라 호출된다.
    • 위의 부모 포인터로 호출하더라도 자식을 가르키기에 자식이 오버라이딩한 함수를 호출한다


  • 부모 클래스에서 가상함수를 선언하면 파생 클래스의 재정의된 함수도 자동으로 가상함수가 된다.
  • 재정의된 함수에 virtual키워드를 표시해도 안해도 상관없다.



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; }  // 컴파일 오류
    };
    



공변 반환형

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; 
    }

};
  • 오버라이딩시 반환형도 같아야하지만 B는 A의 자식이기에 B타입인 B의 this는 A 성질도 있어서
  • this를 리턴하는 함수의 경우 리턴 타입이 달라도 오버라이딩이 허용된다.



virtual 소멸자

  • 부모 타입의 포인터가 자식 타입의 객체를 참조하고 있을 때 delete를 해주면
  • 자식 소멸자는 호출 되지 않고 부모 소멸자만 호출된다.
    • 자식을 지우면 (자식 소멸자 -> 부모 소멸자) 순서로 부모 소멸자까지 호출되지만
    • 부모를 지우면 부모 소멸자만 호출되어 자식 소멸자는 호출되지 않는다.
      • 상속에 있어서 부모는 자식을 포함하지 않으므로…
  • 왜 자식 소멸자가 호출 안됨?
    • 가상 함수로 정의되지 않은 오버라이딩 함수(소멸자)라서 부모 클래스 소멸자가 호출됨.
      • 그래서 소멸자를 가상함수로 만들어주면 해결된다.

상속이 될 또는 상속이 될 거 같은 클래스의 소멸자는 그냥 virtual로 만들자.



개인 공부 기록용 블로그입니다.
틀린 부분 있으다면 지적해주시면 감사하겠습니다!!

카테고리:

업데이트:

댓글남기기