출처 :
- 명품 C++ Programming (저자 황기태)
- 객체지향프로그래밍
파생 클래스에서 함수를 재정의
| #include <iostream> |
| using namespace std; |
| |
| class Base { |
| public: |
| void f() { cout << "Base:f() called" << end; } |
| }; |
| |
| class Derived : public Base { |
| public: |
| |
| void f() { cout << "Derived:f() called" << endl; } |
| }; |
| |
| void main() { |
| Derived d *pDer; |
| pDer = &d; |
| pDer->f(); |
| |
| Base* pBase; |
| pBase = pDer; |
| pBase->f(); |
| |
가상 함수와 오버라이딩
가상 함수 virtual function
함수 오버라이딩 function overriding
- 파생 클래스에서 기본 클래스의 가상 함수와 동일한 이름의 함수 선언
- 기본 클래스의 가상 함수의 존재감 상실시킴
- 파생 클래스에서 오버라이딩한 함수가 호출되도록 동적 바인딩
- 함수 재정의라고도 부름
- 다형성의 한 종류
오버라이딩과 가상 함수 호출
| 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(); |
| Base * pBase; |
| pBase = pDer; |
| pBase->f(); |
| } |
오버라이딩의 목적
동적 바인딩
- 파생 클래스에 대해 기본 클래스에 대한 포인터로 가상 함수를 호출하는 경우
- 객체 내에 오버라이딩한 파생 클래스의 함수를 찾아 실행
- 실행 중에 이루어짐 → 실행시간 바인딩, 런타임 바인딩, 늦은 바인딩
오버라이딩된 함수를 호출하는 동적 바인딩
| class Shape { |
| public: |
| void paint() { draw(); } |
| virtual void draw() { cout << "Shape::draw() called" << endl; } |
| }; |
| |
| class Circle : public Shape { |
| public: |
| virtual void draw() { cout << "Circle::draw() called" << endl; } |
| }; |
| |
| int main() { |
| Shape *pShape = new Circle(); |
| pShape->paint(); |
| delete pShape; |
| } |
C++ 오버라이딩의 특징
- 오버라이딩의 성공 조건
- 가상 함수 이름, 매개 변수 타입과 개수, 리턴 타입이 모~~두 일치
- 오버라이딩 시 파생 클래스에서 virtual 지시어 생략 가능
- 가상 함수의 접근 지정 private, protected, public 중 자유롭게 지정
상속이 반복되는 경우 가상 함수 호출
| class Base { |
| public: |
| virtual void f() { cout << "Base::f() called" << endl; } |
| }; |
| |
| class Derived : public Base { |
| public: |
| void f() { cout << "Derived::f() called" << endl; } |
| }; |
| |
| class GrandDerived : public Derived { |
| public: |
| void f() { cout << "GrandDerived::f() called" << endl; } |
| }; |
| |
| int main() { |
| GrandDerived g; |
| Base *bp; |
| Derived *dp; |
| GrandDerived *gp; |
| |
| bp = dp = gp = &g; |
| |
| |
| bp->f(); |
| dp->f(); |
| gp->f(); |
| } |
오버라이딩과 범위 지정 연산자 ::
- 범위 지정 연산자
- 정적 바인딩 지시
- 기본클래스::가상함수() 형태로 기본 클래스의 가상 함수를 정적으로 호출
| class Shape { |
| public: |
| virtual void draw() { cout << "--Shape--"; } |
| }; |
| |
| class Circle : public Shape { |
| public: |
| virtual void draw() { |
| |
| Shape::draw(); |
| cout << "Circle" << endl; |
| } |
| }; |
| |
| int main() { |
| Circle circle; |
| Shape * pShape = &circle; |
| |
| pShape->draw(); |
| |
| pShape->Shape::draw(); |
| } |
가상 소멸자
- 소멸자를 virtual 키워드로 선언
- 소멸자 호출 시 동적 바인딩 발생
| class Base { |
| public: |
| virtual ~Base(); |
| }; |
| |
| class Derived: public Base { |
| public: |
| virtual ~Derived(); |
| }; |
| |
| int main() { |
| Base *p = new Derived(); |
| delete p; |
| |
| |
| |
| } |
| |
| |
소멸자를 가상 함수로 선언
| 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; |
| delete bp; |
| } |
| |
| |
| |
가상 함수와 오버라이딩 활용 사례
가상 함수를 가진 기본 클래스의 목적 (기출)
| #include <iostream> |
| #include "Shape.h" |
| #include "Circle.h" |
| #include "Rect.h" |
| #include "Line.h" |
| using namespace std; |
| |
| int main() { |
| Shape *pStart=NULL; |
| Shape *pLast; |
| pStart = new Circle(); |
| pLast = pStart; |
| pLast = pLast->add(new Rect()); |
| pLast = pLast->add(new Circle()); |
| pLast = pLast->add(new Line()); |
| pLast = pLast->add(new Rect()); |
| |
| |
| Shape* p = pStart; |
| while(p != NULL) { |
| |
| p->paint(); |
| p = p->getNext(); |
| } |
| |
| |
| p = pStart; |
| while(p != NULL) { |
| |
| Shape* q = p->getNext(); |
| |
| delete p; |
| |
| p = q; |
| } |
| } |
| |
| Circle |
| Rectangle |
| Circle |
| Line |
| Rectangle |
순수 가상 함수
- 기본 클래스의 가상 함수 목적
- 파생 클래스에서 재정의할 함수를 알려주는 역할
- 실행할 코드를 작성할 목적이 아님
- 굳이.. 기본 클래스의 가상 함수를 구현할 필요가 있을까??
- 순수 가상 함수
- 함수의 코드가 없고 선언만 있는 가상 멤버 함수
| class Shape { |
| public: |
| virtural void draw = 0; |
| }; |
추상 클래스
- 하나 이상의 순수 가상 함수를 가진 클래스
- 온전한 클래스가 아니므로 객체 생성 불가능
- 추상 클래스 포인터는 선언 가능 - 동적 바인딩하기 위해
추상 클래스의 목적
- 추상 클래스의 인스턴스를 생성할 목적 아님
- 상속에서 기본 클래스의 역할을 하기 위함
- 순수 가상 함수를 통해 파생 클래스의 구현할 함수의 원형을 보여주는 인터페이스 역할
- 추상 클래스의 모든 멤버 함수를 순수 가상함수로 선언할 필요는 없음
추상 클래스의 상속과 구현
- 추상 클래스를 단순 상속하면 자동 추상 클래스
| class Shape { |
| public: |
| virtual void draw() = 0; |
| }; |
| |
| class Circle : public Shape { |
| public: |
| string toString() { return "Circle 객체"; } |
| }; |
- 추상 클래스를 상속받은 파생 클래스를 추상 클래스로 만들지 않으려면 순수 가상 함수를 오버라이딩해야함
| class Shape { |
| public: |
| virtual void draw() = 0; |
| }; |
| |
| class Circle : public Shape { |
| public: |
| |
| |
| virtual void draw() { |
| cout << "Circle"; |
| } |
| string toString() { return "Circle 객체"; } |
| }; |
추상 클래스 구현 연습
| class Calculator { |
| public: |
| virtual int add(int a, int b) = 0; |
| virtual int subtract(int a, int b) = 0; |
| virtual double average(int a [], int size) = 0; |
| }; |
| |
| class GoodCalc : public Calculator { |
| public: |
| int add(int a, int b) { return a + b; } |
| int subtract(int a, int b) { return a - b; } |
| double average(int a [], int size) { |
| double sum = 0; |
| for(int i=0; i<size; i++) |
| sum += a[i]; |
| return sum/size; |
| } |
| }; |
| |
| int main() { |
| int a[] = {1,2,3,4,5}; |
| Calculator *p = new GoodCalc(); |
| cout << p->add(2, 3) << endl; |
| cout << p->subtract(2, 3) << endl; |
| cout << p->average(a, 5) << endl; |
| delete p; |
| } |
| #include <iostream> |
| using namespace std; |
| |
| class Calculator { |
| void input() { |
| cout << "정수 2 개를 입력하세요>> "; |
| cin >> a >> b; |
| } |
| protected: |
| int a, b; |
| virtual int calc(int a, int b) = 0; |
| public: |
| void run() { |
| input(); |
| cout << "계산된 값은 " << calc(a, b) << endl; |
| } |
| }; |
| |
| class Adder : public Calculator { |
| protected: |
| |
| int calc(int a, int b) { return a + b; } |
| }; |
| |
| class Subtractor : public Calculator { |
| protected: |
| |
| int calc(int a, int b) { return a - b; } |
| }; |
| |
| int main() { |
| Adder adder; |
| Subtractor subtractor; |
| adder.run(); |
| subtractor.run(); |
| } |