CH09 가상 함수와 추상 클래스

jiyoon·2024년 6월 12일

1. 가상 함수와 함수 재정의

1) 정의

가상 함수

  • 가상 함수(virtual function): virtual 키워드로 선언된 멤버 함수로, 함수 호출을 실행 시간까지 미루도록 지시하여 동적 바인딩을 가능하게 함.
  • 동적 바인딩: 실행 시간에 함수 호출을 결정, 이를 통해 다형성을 실현.

함수 오버라이딩

  • 함수 오버라이딩(function overriding): 파생 클래스에서 기본 클래스의 가상 함수와 동일한 이름의 함수를 선언하여 기본 클래스의 가상 함수를 재정의하는 것. 파생 클래스에서 오버라이딩한 함수가 호출되도록 동적 바인딩.
  • 목적: 다형성을 제공하여, 파생 클래스의 객체를 통해 호출 시 파생 클래스의 함수를 실행.
  • 특징
    - 가상 함수 이름 , 매개 변수 타입과 개수 , 리턴 타입이 모두 일치
    - 파생 클래스에서 virtual 생략 가능 . virtual 지시어도 상속됨

함수 재정의

  • 함수 재정의 : 가상 함수를 재정의 하는 것이 아니라 virtual을 쓰지 않은 함수를 재정의한 경우, 컴파일 시간에 결정되어 단순히 호출되도록 정적 바인딩.

2) 예제 9-2 : 동적 바인딩과 정적 바인딩

class Base {
public:
    virtual void virtualFunc() { cout << "Base::virtualFunc() called" << endl; } // 가상 함수
    void nonVirtualFunc() { cout << "Base::nonVirtualFunc() called" << endl; }   // 비가상 함수
};

class Derived : public Base {
public:
    void virtualFunc() override { cout << "Derived::virtualFunc() called" << endl; } // 가상 함수 오버라이딩
    void nonVirtualFunc() { cout << "Derived::nonVirtualFunc() called" << endl; }    // 함수 재정의
};

int main() {
    Derived d;
    Base* pBase = &d;  // 업캐스팅

    // 동적 바인딩: Derived 클래스의 가상 함수가 호출됨
    pBase->virtualFunc();  // 출력: Derived::virtualFunc() called

    // 정적 바인딩: Base 클래스의 비가상 함수가 호출됨
    pBase->nonVirtualFunc();  // 출력: Base::nonVirtualFunc() called

    return 0;
}

3) 예제 : 오버라이딩을 통한 다형성


4) 예제 : 기본 클래스에서 파생 클래스의 함수를 호출


5) 점위 지정 연산자(::)를 이용한 기본 클래스의 가상 함수 호출

기본클래스::가상함수()형태로 기본 클래스의 가상 함수를 정적 바인딩으로 호출. 참고로, 정적 바인딩은 컴파일 시간에 결정되는 것이고, 동적 바인딩은 실행 시간에 결정되는 것이다.



3. 가상 소멸자

가상 소멸자의 필요성

  • 가상 소멸자: 소멸자를 virtual 키워드로 선언하여 동적 바인딩을 통해 파생 클래스의 소멸자가 호출되도록 함.
  • 목적: 기본 클래스의 포인터를 통해 파생 클래스 객체를 삭제할 때, 파생 클래스의 소멸자가 호출되어야 리소스 누수를 방지할 수 있음.
class Base {
public:
    virtual ~Base() { cout << "~Base() called" << endl; }
};

class Derived : public Base {
public:
    ~Derived() { cout << "~Derived() called" << endl; }
};

int main() {
    Base* p = new Derived();
    delete p;  // ~Derived() -> ~Base() 호출
}

4. 추상 클래스와 순수 가상 함수

추상 클래스

  • 추상 클래스: 최소 하나의 순수 가상 함수를 가진 클래스. 직접 객체를 생성할 수 없음.
  • 목적: 상속을 통해 파생 클래스에서 구현할 함수의 인터페이스를 제공.
class Shape {
public:
    virtual void draw() = 0;  // 순수 가상 함수
};

class Circle : public Shape {
public:
    void draw() override { cout << "Circle" << endl; }
};

int main() {
    Shape* p = new Circle();
    p->draw();  // Circle 출력
    delete p;
}

5. 예제

예제 9-1: 파생 클래스에서 함수 재정의

#include <iostream>
using namespace std;

class Base {
public:
    void f() { cout << "Base::f() called" << endl; }
};

class Derived : public Base {
public:
    void f() { cout << "Derived::f() called" << endl; }
};

int main() {
    Derived d;
    d.f();  // Derived::f() 호출

    Base* pBase = &d;  // 업캐스팅
    pBase->f();  // Base::f() 호출
}

예제 9-2: 오버라이딩과 가상 함수 호출

#include <iostream>
using namespace std;

class Base {
public:
    virtual void f() { cout << "Base::f() called" << endl; }
};

class Derived : public Base {
public:
    void f() override { cout << "Derived::f() called" << endl; }
};

int main() {
    Derived d;
    Base* pBase = &d;  // 업캐스팅
    pBase->f();  // Derived::f() 호출 (동적 바인딩)
}

예제 9-3: 상속이 반복되는 경우 가상 함수 호출

#include <iostream>
using namespace std;

class Base {
public:
    virtual void f() { cout << "Base::f() called" << endl; }
};

class Derived : public Base {
public:
    void f() override { cout << "Derived::f() called" << endl; }
};

class GrandDerived : public Derived {
public:
    void f() override { cout << "GrandDerived::f() called" << endl; }
};

int main() {
    GrandDerived g;
    Base* pBase = &g;
    Derived* pDerived = &g;
    GrandDerived* pGrand = &g;

    pBase->f();  // GrandDerived::f() 호출 (동적 바인딩)
    pDerived->f();  // GrandDerived::f() 호출 (동적 바인딩)
    pGrand->f();  // GrandDerived::f() 호출
}

예제 9-4: 범위 지정 연산자(::)를 이용한 기본 클래스의 가상 함수 호출

#include <iostream>
using namespace std;

class Shape {
public:
    virtual void draw() { cout << "--Shape--" << endl; }
};

class Circle : public Shape {
public:
    void draw() override {
        Shape::draw();  // 기본 클래스의 draw() 호출
        cout << "Circle" << endl;
    }
};

int main() {
    Circle circle;
    Shape* pShape = &circle;
    pShape->draw();  // --Shape--Circle 출력 (동적 바인딩)
    pShape->Shape::draw();  // --Shape-- 출력 (정적 바인딩)
}

예제 9-6: 소멸자를 가상 함수로 선언

#include <iostream>
using namespace std;

class Base {
public:
    virtual ~Base() { cout << "~Base()" << endl; }
};

class Derived : public Base {
public:
    ~Derived() { cout << "~Derived()" << endl; }
};

int main() {
    Derived* dp = new Derived();
    Base* bp = new Derived();

    delete dp;  // ~Derived() -> ~Base() 호출
    delete bp;  // ~Derived() -> ~Base() 호출
}

예제 9-7: 추상 클래스를 상속받는 파생 클래스 구현 연습

#include <iostream>
using namespace std;

class Calculator {
protected:
    int a, b;
    virtual int calc(int a, int b) = 0;  // 순수 가상 함수
public:
    void input() {
        cout << "정수 2개를 입력하세요>> ";
        cin >> a >> b;
    }
    void run() {
        input();
        cout << "계산된 값은 " << calc(a, b) << endl;
    }
};

class Adder : public Calculator {
protected:
    int calc(int a, int b) override {
        return a + b;
    }
};

class Subtractor : public Calculator {
protected:
    int calc(int a, int b) override {
        return a - b;
    }
};

int main() {
    Adder adder;
    Subtractor subtractor;

    adder.run();  // 덧셈 결과 출력
    subtractor.run();  // 뺄셈 결과 출력
}
profile
주니어 개발자

0개의 댓글