11.1 가상함수
상속 관계에서 함수를 중복하는 경우
가상 함수와 오버라이딩
가상 함수(virtual function)
- virtual 키워드로 선언된 멤버 함수
- virtual 키워드의 의미
- 동적 바인딩 지시어
- 컴파일러에게 함수에 대한 호출 바인딩을 실행 시간까지 미루도록 지시
class Base{
public:
virtual void f(); // f()는 가상 함수
};
함수 오버라이딩(function overriding)
- 파생 클래스에서 기본 클래스의 가상 함수와 동일한 이름의 함수 선언, 내부 로직 빼고는 완전히 동일해야 함
- 기본 클래스의 가상 함수의 존재감을 상실시킴
- 파생 클래스에서 오버라이딩 함수가 호출되도록 동적 바인딩
- 함수 재정의라고도 부름
- 다형성의 한 종류
오버라이딩과 가상 함수 호출
오버라이딩: 파생 클래스에서 구현할 함수 인터페이스 제공
다형성 실형 예:
- draw() 가상 함수를 가진 기본 클래스 Shape
- 오버라이딩을 통해 Circle, Rect, Line 클래스에서 자신만의 draw() 구현
동적 바인딩
동적 바인딩
- 파생 클래스에 대해, 기본 클래스에 대한 포인터로 가상 함수를 호출하는 경우
- 객체 내에 오버라이딩한 파생 클래스의 함수를 찾아 실행
- 실행 중에 이루어짐: 실행시간 바인딩, 런타임 바인딩, 늦은 바인딩으로 불림
C++ 오버라이딩의 특징
오버라이딩의 성공 조건
- 가상 함수 이름, 매개 변수 타입과 개수, 리턴 타입이 모두 일치
- 오버라이딩 시 virtual 지시어 생략 가능
- 가상 함수의 virtual 지시어는 상속됨, 파생 클래스에서 virtual 생략 가능
- 가상 함수의 접근 지정
- private, protected, public 중 자유롭게 지정 가능
상속이 반복되는 경우 가상 함수 호출
Base, Derived, GrandDerived가 상속 관계에 있을 때,
오버라이딩과 범위 지정 연산자(::)
범위 지정 연산자(::)
- 정적 바인딩 지시
- 기본클래스::가상함수() 형태로 기본 클래스의 가상 함수르 정적 바인딩으로 호출
범위 지정 연산자(::)를 이용한 기본 클래스의 가상 함수 호출
가상 소멸자
소멸자도 가상함수로 선언해야 하는 경우
- 클래스에 가상함수가 있고, 클래스가 반드시 소멸자를 사용해야 할 때
ex) 동적으로 생성한 파생 클래스의 객체를 기본 클래스 포인터로 가리킬 때
-> 기본 클래스 포인터로 delete 하더라도 파생 클래스의 소멸자가 호출되도록 해야 함
- 가상 소멸자
- 소멸자를 virtual 키워드로 선언
- 소멸자 호출 시 동적 바인딩 발생
Shape* pShape = new Rectangle;
delete pShape; // Rectangle 소멸자가 호출되도록 하려면 Shape 소멸자를 가상함수로 만든다.
class Shape{
...
virtual void Draw() const;
...
virtual ~Shape();
};
가상 소멸자
소멸자를 가상함수로 선언
오버로딩과 오버라이딩 비교
비교 요소 | 오버로딩 | 오버라이딩 |
정의 | 매개 변수 타입이나 개수가 다르지만, 이름이 같은 함수들이 중복 작성되는 것 | 기본 클래스에 선언된 가상 함수를 파생 클래스에서 이름, 매개 변수 타입, 매개 변수 개수, 리턴 타입까지 완벽히 같은 원형으로 재작성하는 것 |
존재 | 외부 함수들 사이. 한 클래스의 멤버들. 상속 관계 | 상속 관계. 가상 함수에서만 적용 |
목적 | 이름이 같은 여러 개의 함수를 중복 작성하여 사용의 편의성 향상 | 기본 클래스에 구현된 가상 함수를 무시하고, 파생 클래스에서 새로운 기능으로 재정의하고자 함 |
바인딩 | 정적 바인딩. 컴파일 시에 중복된 함수들의 호출 구분 | 동적 바인딩. 실행 시간에 오버라이딩된 함수를 찾아 실행 |
관련 객체지향 특성 | 다형성 | 다형성 |
11.2 가상함수와 오버라이딩 활용사례
가상 함수를 가진 기본 클래스의 목적
가상 함수 오버라이딩
파생 클래스마다 다르게 구현하는 다형성
void Circle::draw(){cout<<"Circle"<<endl;}
void Rect::draw(){cout<<"Rectangle"<<endl;}
void Line::draw(){cout<<"Line"<<endl;}
파생 클래스에서 가상 함수 draw()의 재정의
- 어떤 경우에도 자신이 만든 draw()가 호출됨을 보장 받음
- 동적 바인딩에 의해
동적 바인딩 실행: 파생 클래스의 가상 함수 실행
main() 함수가 실행될 때 구성된 객체의 연결
기본 클래스의 포인터 활용
기본 클래스의 포인터로 파생 클래스 접근
- pStart, pLast, p 타입이 Shape*
- 연결 리스트를 따라 Shape을 상속받은 파생 객체들 접근
- p->paint()의 간단한 호출로 파생 객체에 오버라이딩된 draw() 함수 호출
11.3 추상 클래스와 인터페이스 상속
순수 가상함수
기본 클래스의 가상 함수 목적
- 파생 클래스에서 재정의할 함수를 알려주는 역할
- 실행할 코드를 작성할 목적이 아님
- 기본 클래스의 가상 함수를 굳이 구현할 필요는 없음(필요에 따라)
순수 가상 함수
- 함수의 코드가 없고 선언만 있는 가상 멤버 함수
- 파생 클래스에서 재정의될 함수에 대해서 미리 원형이 필요하기는 하지만 기본 클래스에서는 아직 구현할 필요가 없을 때 사용
- 순수 가상 함수는 "파생 클래스에서 재정의하도록 원형만 미리 준비해두는 것"이라는 의미가 되며, 파생 클래스는 상속받은 순수 가상 함수를 반드시 재정의해야 한다.
순수 가상 함수 선언 방법
- 함수 선언 끝 부분에 "=0"을 적어줌
class Shape{
public:
virtual void draw() = 0; // 순수 가상 함수
};
추상 클래스
추상 클래스(abstract class)
- 최소한 하나의 순수 가상 함수를 갖는 클래스
class Shape{ // Shape은 추상 클래스
protected:
int _x;
int _y;
public:
void paint();
virtual void draw()=0; // 순수 가상 함수
};
void Shape::paint(){
draw(); // 순수 가상 함수라도 호출은 할 수 있다.
}
추상 클래스 특징
- 온전한 클래스가 아니므로 추상 클래스는 객체를 생성할 수 없다.
Shape shape; // 컴파일 오류
Shape *p = new Shape(); // 컴파일 오류
- 추상 클래스의 포인터 변수나 레퍼런스 변수는 정의할 수 있다.
- 파생 클래스 객체를 가리키는 용도로 사용
- 파생 클래스 객체에 접근하는 인터페이스 역할 제공
Shape *p;
추상 클래스의 목적
추상 클래스의 목적
- 추상 클래스의 인스턴스를 생성할 목적이 아님
- 상속에서 기본 클래스의 역할을 하기 위함
- 순수 가상 함수를 통해 파생 클래스에서 구현할 함수의 형태(원형)을 보여주는 인터페이스 역할
- 추상 클래스의 모든 멤버 함수를 순수 가상 함수로 선언할 필요 없음
추상 클래스의 상속과 구현
추상 클래스의 상속
- 추상 클래스를 단순 상속하면 자동 추상 클래스
추상 클래스의 구현
- 추상 클래스를 상속받아 순수 가상 함수를 오버라이딩
- 파생 클래스는 추상 클래스가 아님
Shape을 추상 클래스로 수정
구현 상속과 인터페이스 상속
구현상속
- 기본 클래스가 해당 함수의 구현을 제공
- 클래스의 멤버 함수가 일반 함수라면 파생 클래스에서는 이 함수를 재정의할 필요가 없다.
디폴트 구현 상속 + 인터페이스 상속
- 파생 클래스가 해당 함수를 재정의할지 여부 선택
- 클래스의 멤버 함수가 가상 함수라면 파생 클래스에서는 이 함수를 재정의할 수도 있고, 재정의하지 않을 수도 있다.
인터페이스 상속
- 클래스의 멤버 함수가 순수 가상 함수라면 파생 클래스에서는 이 함수를 반드시 재정의해야 한다.
다양한 종류의 멤버 함수
상속곽 관련해서 지금까지 살펴본 멤버 함수 종류
- 일반적인 멤버 함수
- 가상 함수
- 순수 가상 함수
어떤 종류의 멤버 함수를 사용할 지에 대한 가이드 라인
- 처음엔 그냥 멤버 함수로 만든다.
- 다형성을 이용해야 하는 경우라면 가상 함수로 만든다.
- 다형성을 위해서 함수의 원형만 필요한 경우라면 순수 가상 함수로 만든다.
'공부 > C++' 카테고리의 다른 글
12. 템플릿과 표준 템플릿 라이브러리(STL) (0) | 2024.06.06 |
---|---|
10. 상속 (0) | 2024.05.27 |
9. friend와 연산자 중복 (0) | 2024.05.16 |
8. 함수와 참조, 복사생성자 (1) | 2024.05.14 |
7. 여러가지 객체의 생성방법 (0) | 2024.05.14 |