클래스에서 파생 클래스가 부모 클래스의 함수를 재정의 하여 사용 하는 것
class Base
{
public:
void f()
{
cout << "Base" << endl;
}
};
class Derived : public Base
{
public :
void f()
{
cout << "Derived" << endl;
}
};
Derived d,*Pder;
Pder = &d;
Pder->f();
Pder->Base::f();
이렇게 할 경우 원하는 대로 나옴
<업캐스팅>
Base* Pba = Pder;
Pba->f();
이렇게 할 경우 파생 클래스 함수가 아닌 부모 클래스 함수가 실행됨.
파생 클래스에서 부모 클래스에서 정의한 가상 함수를 재정의, 부모 클래스에서 정의한 가상함수를 무력화 하는 것..
즉 위 두 실행 방법에서 어떻게 실행 하든 상관없이 파생 클래스에서 재정의 된 함수로 실행됨..
virtual: 컴파일러에게 자신에 대한 호출 바인딩을 실행 시간까지 미루는 키워드
class Base
{
public:
virtual void f() // 가상 함수 선언
{
cout << "Base" << endl;
}
};
Derived d,*Pder;
Pder = &d;
Pder->f();
Base* Pba = Pder;
Pba->f();
둘 다 파생 클래스의 함수로 실행된다.
class Base
{
public:
virtual void f() // 가상 함수 선언
{
cout << "Base" << endl;
}
};
class Derived : public Base
{
public :
virtual void f() override //override될 가상 함수
{
cout << "Derived" << endl;
}
};
이때 override 지시어를 함수 뒤에 적는게 좋다.
컴파일러가 override할 함수를 찾아 오류를 발견하는데 도움이 됨.
- 가상 함수의 이름과 매개변수 타입, 개수 반환형이 일치 해야함.
- 오버 라이딩시, 파생 클래스에서 virtual 키워드는 생략 가능.
- 가상 함수의 접근 지정이 자유롭다.
추가) 오버 라이딩으로 무력화 된 부모 클래스의 함수를 실행 하는 법 => 범위 연산자(::) 사용
Derived d;
Base* Pba = &d;
Pba->f();
Pba->Base::f(); //부모 클래스 함수 호출 가능!
override는 동적 바인딩으로 실행된다.
동적 바인딩(Dynamic Binding) = 실행 시간 바인딩(run tume binding) = 늦은 바인딩(late binding)
=> 가상함수를 호출하는 코드를 컴파일 할 때, 바인딩을 실행 시간에 결정하도록 미룬다. 나중에 가상함수가 호출되면 실행 중 객체 내에 오버라이딩된 가상함수를 동적으로 찾아 호출한다.
<용도>
1. 클래스 상속 금지
2. 가상 함수 오버라이딩 무력화
class Base final //상속 금지
{
public:
virtual void f() final// f()의 오버 라이딩 무력화
{
cout << "Base" << endl;
}
};
class Derived : public Base // 상속 불가능이므로 오류 발생
{
public :
virtual void f() override //오버 라이딩이 불가능하므로 오류 발생
{
cout << "Derived" << endl;
}
};
가상 소멸자 선언을 추천하는 이유
=> 부모 클래스에 대한 포인터로 파생 클래스의 객체를 delete하는 경우 정상적인 소멸을 하기 위해서이다.
class Base
{
public:
~Base()
{
cout << "Base" << endl;
}
};
class Derived : public Base
{
public :
~Derived()
{
cout << "Derived" << endl;
}
};
int main()
{
Base* Pba = new Derived();
delete Pba; //Base 소멸자만 실행됨.
return 0;
}
class Base
{
public:
virtual ~Base() // 가상 소멸자 선언
{
cout << "Base" << endl;
}
};
class Derived : public Base
{
public :
virtual ~Derived()
{
cout << "Derived" << endl;
}
};
int main()
{
Base* Pba = new Derived();
delete Pba; // Derived -> Base 소멸자 호출
return 0;
}
파생 클래스에서 재정의 해야할 함수를 알려주는 interface의 역할
class Base
{
public:
virtual void f() = 0; //순수 가상함수 선언
};
상속 받을 경우 무조건 재정의를 해야한다.
최소 하나의 순수 가상함수를 포함한 불완전힌 클래스 , 인스턴스를 생성할 수 없다.
=> 파생 클래스가 무조건 포함해야 하는 함수를 알려주는 인터페이스 용
class Base // 추상 클래스
{
public:
void base() { f(); }
virtual void f() = 0; //순수 가상함수 선언
};
class Derived : public Base //추상 클래스를 상속 받았으므로 추상 클래스
{
};
Base b; //추상클래스 인스턴스화 불가능
Base* Pb; // 포인터로는 선언 가능
Base* Pba = new Derived(); //파생클래스에서 부모 클래스에서 받은 순수 가상 함수를
//정의하지 않을 경우 파생 클래스도 선언 불가능
delete Pba;
class Base // 추상 클래스
{
public:
void base() { f(); }
virtual void f() = 0; //순수 가상함수 선언
};
class Derived : public Base
{
public :
virtual void f() //순수 가상 함수 구현
{
cout << "Derived" << endl;
}
};
int main()
{
Base* Pba = new Derived(); //이제 Derived는 추상 클래스가 아니므로
//파생클래스 인스턴스(객체) 선언 가능
delete Pba;
return 0;
}