1. 가상 함수
1) 가상 함수(virtual function)
- virtual 키워드로 선언된 멤버 함수
- 동적 바인딩 지시어
- 컴파일러에게 함수에 대한 호출 바인딩을 실행 시간까지 미루도록 지시
- 함수 오버라이딩(function overriding)
- 파생 클래스에서 기본 클래스의 가상 함수와 동일한 이름의 함수 선언
- 파생 클래스에서 오버라이딩한 함수가 호출되도록 동적 바인딩
- 기본 클래스의 가상함수를 실행 X
- 오버라이딩과 가상 함수 호출
#include <iostream>
using namespace std;
class Base {
public:
virtual void f() { cout << "Base::f() called" << endl; }
};
class Derived : public Base {
public:
virtual void f() { cout << "Derived::f() called" << endl; }
};
int main()
{
Derived d, * pDer;
pDer = &d;
pDer->f(); // Derived::f() 호출
Base* pBase;
pBase = pDer; // 업 캐스팅
pBase->f(); // 동적 바인딩 발생!! Derived::f() 실행
}
2) 동적 바인딩
- 파생 클래스에 대해, 기본 클래스에 대한 포인터로 가상 함수를 호출하는 경우
- 객체 내에 오버라이딩한 파생 클래스의 함수를 찾아 실행
- 실행 중에 이루어짐
3) 오버라이딩 성공 조건
- 가상 함수 이름, 매개 변수 타입과 개수, 리턴 타입이 모두 일치
- 오버라이딩 시 virtual 키워드 생략 가능
class Base {
public:
virtual void fail();
virtual void success();
virtual void g(int);
};
class Derived : public Base {
public:
virtual int fail(); // 오버라이딩 실패. 리턴 타입이 다름
virtual void success(); // 오버라이딩 성공
virtual void g(int, double); // 오버로딩 사례. 정상 컴파일
};
4) 오버라이딩과 범위 지정 연산자(::)
- 정적 바인딩 지시
- 기본클래스::가상함수() 형태로 기본 클래스의 가상 함수를 정적 바인딩으로 호출
class Shape {
public:
virtual void draw() {
...}
};
class Circle : public Shape {
public:
virtual void draw() {
Shape::draw(); // 기본 클래스의 draw()를 실행한다.
.... // 기능을 추가한다.
}
};
Ex) 범위 지정 연산자를 이용한 기본 클래스의 가상 함수 호출
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() {
cout << "--Shape--";
}
};
class Circle : public Shape {
public:
virtual void draw() {
Shape::draw(); // 기본 클래스의 draw() 호출
cout << "Circle" << endl;
}
};
int main() {
Circle circle;
Shape* pShape = &circle;
pShape->draw(); //동적 바인딩
pShape->Shape::draw(); //정적 바인딩, Shape의 draw()로 지정
}
5) 가상 소멸자
- 소멸자를 virtual 키워드로 선언
- 소멸자 호출 시 동적 바인딩 발생
- 클래스에 가상함수가 있고, 클래스가 반드시 소멸자를 사용해야할 때 선언해줘야함
Ex) 소멸자를 가상함수로 선언
#include <iostream>
using namespace std;
class Base {
public:
virtual ~Base() { cout << "~Base()" << endl; }
};
class Derived : public Base {
public:
virtual ~Derived() { cout << "~Derived()" << endl; }
};
int main() {
Derived* dp = new Derived();
Base* bp = new Derived();
delete dp; // Derived의 포인터로 소멸
delete bp; // Base의 포인터로 소멸
}
2. 추상클래스와 인터페이스 상속
1) 순수 가상함수
- 기본 클래스의 가상 함수 목적
- 파생 클래스에서 재정의할 함수를 알려주는 역할
- 순수 가상함수
- 함수의 코드가 없고, 선언만 있는 가상 멤버 함수
- 파생 클래스에서 오버라이딩될 함수에 대해 원형이 필요하지만, 기본 클래스에서 정의할 필요가 없을 때 사용
- 파생 클래스는 상속받은 순수 가상함수를 반드시 재정의해야함
- 순수 가상함수 선언 방법
- 함수 선언의 끝 부분에 "=0"을 적어둥
2) 추상 클래스(abstract class)
- 최소한 하나의 순수 가상 함수를 갖는 클래스
- 파생 클래스는 반드시 추상 클래스의 순수 가상함수를 구현해야함
class Shape { // Shape은 추상 클래스
protected :
int _x;
int _y;
public:
void paint();
virtual void draw() = 0; // 순수 가상 함수
};
void Shape::paint() {
draw(); // 순수 가상 함수라도 호출은 할 수 있다.
}
- 특징
- 온전한 클래스가 아니므로 추상 클래스는 객체를 생성할 수 없음
- 추상 클래스의 포인터 변수나 레퍼런스 변수는 정의 가능
- 파생 클래스 객체를 가리키는 용도
- 파생 클래스 객체에 접근하는 인터페이스 역할
- 목적
- 상속에서 기본 클래스의 역할을 하기 위함
- 순수 가상함수를 통해 파생 클래스에서 구현할 함수의 원형을 보여주는 인터페이스 역할
- 추상 클래스의 모든 멤버 함수를 가상 함수를 선언할 필요 X
- 상속에서 기본 클래스의 역할을 하기 위함
'프로그래밍 언어 > C++' 카테고리의 다른 글
12) 템플릿 (1) | 2023.12.09 |
---|---|
10) 상속 (0) | 2023.12.09 |
9) friend와 연산자 중복 (0) | 2023.12.08 |
8) 함수와 참조, 복사생성자 (0) | 2023.12.08 |
7) 여러가지 객체의 생성 방법 (1) | 2023.11.03 |