이번주는 가상 함수와 오버라이딩에 대해 공부하는 주차였는데, 포인터에 대한 개념이 확실히 잡혀 있지 않다 보니 이해하기가 조금 어려웠다. C++은 포인터 개념이 이해가 안되면 다음 진도 나갔을 때 확실히 힘겨운 부분이 있는 것 같다. 회사를 다니면서 학교 공부를 따라가야 되다보니 진도에 내가 딸려(?) 가고 있는데,어떻게든 시간을 내서 포인터를 다시 공부해야 겠다는 생각이 들었다. |
1. 용어
- virtual 키워드: 동적 바인딩 지시어, 컴파일러에게 함수에 대한 호출 바인딩을 실행 시간까지 미루도록 지시함
- 동적 바인딩:
파생 클래스에 대해 기본 클래스에 대한 포인터로 가상 함수를 호출하는 경우, 객체 내에 오버라이딩한 파생 클래스의 함수를 찾아 실행= 실행 시간에 바인딩이 일어난다 정도로 이해
2. 오버라이딩
- 가상 함수(virtual function): virtual 키워드로 선언된 멤버 함수
- 함수 오버라이딩(function overiding): 파생 클래스에서 기본 클래스의 가상 함수와 동일한 이름의 함수 선언
기본 클래스의 가상 함수의 존재감을 상실시킴파생 클래스에서 오버라이딩한 함수가 호출되도록 동적 바인딩함수 재정의라고도 불리우며, 다형성의 한 종류- 그냥 간단하게 말하자면, 기본 클래스 말고 파생 클래스가 실행되도록 해주는 것
💡 오버라이딩 시 가상 함수의 virtual 지시어가 상속되기 때문에, 파생 클래스에서는 virtual 지시어 생략 가능 💡 가상 함수의 접근 지정은 private, protected, public 중 자유롭게 지정 가능 |
예시) 오버라이딩과 가상 함수 호출 예제
#include <iostream>
using namespace std;
class Base {
public:
virtual void f() { cout << "Base::f() called" << endl; } // virtual 키워드로 인해 숨겨짐(?)
};
class Derived : public Base {
public:
virtual void f() { cout << "Derived::f() called" << endl; }
};
int main() {
Derived d, *pDer;
pDer = &d
pDer->f();
Base *pBase;
pBase = pDer;
pBase->f(); // 동적 바인딩 발생, Derived::f() 실행
}
2-1. 목적
- 오버라이딩의 목적: 파생 클래스에서 구현할 함수 인터페이스 제공(= 파생 클래스의 다형성 실현)
예시)
draw()
가상 함수를 가진 기본 클래스Shape
- 오버라이딩을 통해
Circle
,Rect
...등 클래스에서 자신만의draw()
구현
=draw()
를 실행하는데 나만의 shape을 가진 것
▼ 기본 클래스 Shape
class Shape {
protected:
virtual void draw() {} // 가상 함수 선언. 파생 클래스에서 재정의할 "인터페이스" 역할
}
▼ 파생 클래스 Circle - 오버라이딩, 다형성 실현
class Circle : public Shape {
protected:
virtual void draw() {
//circle을 그린다.
}
}
2-2. 성공 조건
- 가상 함수 이름, 매개 변수 타입과 개수, 리턴 타입이 모두 일치할 때
class Base {
public:
virtual void fail();
viirtual void success();
};
class Derived : public Base {
public:
virtual int fail(); // 리턴 타입이 달라 오버라이딩 실패
virtual void success(); // 오버라이딩 성공
};
2-3. 정적 바인딩을 지시하는 범위 지정 연산자(::)
동적 바인딩이 일어날 때 지정을 해서 정적 바인딩도 가능
- 범위 지정 연산자(::)
- 정적 바인딩을 지시
기본 클래스::가상함수()
형태로 기본 클래스의 가상 함수를 정적 바인딩으로 호출
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(); // 정적 바인딩
}
2-4. 오버로딩 vs 함수 재정의 vs 오버라이딩 비교
아직 조금 헷갈리지만, 조금 더 익숙해진 후에는 하기에 기재한 교재 내용을 나만의 워딩으로 바꿀 것.
비교 요소 | 오버로딩 | 함수 재정의 | 오버라이딩 |
정의 | 매개 변수 타입, 개수가 다르지만 이름이 같은 함수가 중복 작성 |
기본 클래스의 멤버 함수를 파생 클래스에서 이름, 매개 변수 타입과 개수, 리턴 타입까지 완벽히 같은 원형으로 재작성하는 것 |
기본 클래스의 가상 함수를 파생 클래스에서 이름, 매개 변수 타임과 개수, 리턴 타입까지 완벽히 같은 원형으로 재작성하는 것 |
존재 | 클래스의 멤버들 사이, 외부 함수들 사이, 기본 클래스&파생 클래스 사이에 존재 가능 |
상속 관계 | 상속 관계 |
목적 | 이름이 같은 여러 개의 함수를 중복 작성하여 사용 편의성 향상 |
기본 클래스 멤버 함수와 별도로 파생 클래스에서 필요하여 작성 |
기본 클래스에서 구현된 가상 함수를 무시하고 파생 클래스에서 새로운 기능으로 재작성하고자 함 |
바인딩 | 정적 바인딩 컴파일 시에 중복된 함수들의 호출 구분 |
정적 바인딩 컴파일 시에 함수의 호출 구분 |
동적 바인딩. 실행 시간에 오버라이딩된 함수를 찾아 실행 |
객체 지향 특성 | 컴파일 시간 다형성 | 컴파일 시간 다형성 | 실행 시간 다형성 |
3. 정리
- 파생 클래스에서 기본 클래스의 가상 함수와 동일한 타입의 함수를 재작성하는 것이 함수 오버라이딩
- 가상함수란 virtual 키워드로 선언된 멤버 함수로서, 컴파일러에게 자신에 대한 호출을 실행시간까지 미루도록 지시
- 파생 클래스에서 가상 함수를 오버라이딩하고 기본 클래스의 포인터로 파생 클래스의 객체를 가리킬 때, 가상 함수를 호출하면 무조건 파생 클래스에서 오버라이딩한 가상 함수가 호출된는 것이 동적 바인딩
'💻 프로그래밍 > C++' 카테고리의 다른 글
[C++] 클래스 상속 (0) | 2022.07.18 |
---|