[C++] 가상함수에 대해

2024. 5. 26. 18:23·C++/Basic

가상함수에 대해서

- 가상 함수는 기본 클래스에서 선언된 함수로, 파생클래스에서 이 함수를 재정의(override)할 수 있게 합니다.

 

어떨 때 쓰이는가?

- 다형성을 구현하기 위해 사용되며, 포인터나 참조를 통해 기본 클래스 형식으로 호출된 함수가 실제 객체의 타입에 따라 적절한 파생 클래스의 함수를 호출할 수 있게 합니다.

 

가상 함수의 기본 구문

- 가상 함수를 선언하려면 기본 클래스에서 함수 선언 앞에 'virtual' 키워드를 사용합니다.

- 파생 클래스에서 동일한 함수 시그니처로 함수를 재정의하면, 이 함수는 가상 함수로 동작합니다.

class Base {
public:
    virtual void show() {
        std::cout << "Base class show function" << std::endl;
    }
};

class Derived : public Base {
public:
    virtual void show() override {
        std::cout << "Derived class show function" << std::endl;
    }
};

 

동작 원리

가상 함수 테이블

- 가상 함수는 가상함수 테이블을 통해 구현됩니다. 가상함수 테이블은 가상 함수 포인터의 배열로, 각 객체의 가상 함수 포인터가 이 테이블을 가리킵니다.

- 각 클래스는 자신의 가상함수 테이블을 가지고 있으며, 가상 함수 호출 시 가상함수 테이블을 통해 적절한 함수가 호출됩니다.

- 컴파일러는 클래스가 가상 함수를 포함하고 있으면 해당 클래스에 대한 가상 함수 테이블을 생성합니다.

- 가상 함수를 호출할 때, 객체의 가상 함수 포인터를 통해 가상 함수 테이블을 참조합니다.

실행 시간 결정

- 가상 함수 호출은 실행 시간(런타임 시간)에 결정됩니다. 객체의 실제 타입에 따라 적절한 함수가 호출됩니다.

- 런타임 시간에 호출을 결정하는 이것을 동적 바인딩이라고 합니다.

  • 가상 함수 테이블(V-Table):
    • 클래스당 하나씩 생성됩니다.
    • 클래스의 모든 객체가 동일한 V-Table을 공유합니다.
    • V-Table은 클래스의 가상 함수 주소들을 저장합니다.
  • 가상 함수 포인터(V-Ptr):
    • 각 객체마다 하나씩 존재합니다.
    • 각 객체의 V-Ptr은 해당 객체의 클래스의 V-Table을 가리킵니다.
    • V-Ptr은 객체의 메모리 내에 저장되며, 64비트 시스템에서는 8바이트를 차지합니다.
    • 가상 함수를 많이 선언해도 각 객체는 V-Ptr 하나만을 가지므로 추가적인 메모리 소모는 8바이트로 동일합니다.
Base* b;
Derived d;
b = &d;

b->show(); // "Derived class show function" 출력

 

위의 코드는 런타임에 객체의 실제 타입이 지정되어 파생클래스의 함수가 호출되는 것을 보여줍니다.

 

가상 소멸자

- 기본 클래스의 소멸자가 가상 함수가 아니면, 파생 클래스의 소멸자가 호출되지 않을 수 있습니다. 이는 메모리 누수를 일으킬 수 있습니다. 따라서 상속 계층에서 동적 메모리 할당을 사용하는 경우, 기본 클래스의 소멸자를 가상으로 선언해야 합니다.

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

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

Base* b = new Derived();
delete b; // "Derived destructor"와 "Base destructor"가 모두 호출됨

 

위의 코드는 파생클래스의 객체를 가지고 있는 기본 클래스의 가상 소멸자 호출을 보여줍니다.

 

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

- 순수 가상함수는 함수 선언에 ' = 0 ' 을 사용해서 선언됩니다. 이는 해당 함수가 구현되지 않았음을 의미하며, 파생클래스에서 반드시 재정의해야 합니다.

class Abstract {
public:
    virtual void show() = 0; // 순수 가상 함수
};

class Concrete : public Abstract {
public:
    void show() override {
        std::cout << "Concrete class show function" << std::endl;
    }
};

 

- 순수 가상 함수를 하나 이상 포함하는 클래스는 추상 클래스가 되며, 이 클래스는 인스턴스화할 수 없습니다.

- 추상 크래스는 구체적인 구현을 가지지 않는 인터페이스 역할을 하며, 이를 상속받는 파생 클래스에서 구체적인 구현을 제공해야 합니다.

- 인스턴스화란 클래스를 기반으로 객체를 생성하는 것을 말합니다. 추상 클래스는 순수 가상 함수를 포함하고 있어, 이 함수의 구현이 없기 때문에 객체를 생성할 수 없습니다. 만약 추상 클래스를 인스턴스화 하려고 하면 컴파일 에러가 발생합니다.

// 추상 클래스는 인스턴스화할 수 없음
Abstract a; // 컴파일 에러

// 파생 클래스를 인스턴스화
Concrete c;

// 추상 클래스의 포인터로 파생 클래스 객체를 가리킬 수 있음
Abstract* aPtr = &c;
aPtr->pureVirtualFunction(); // "Concrete implementation of pureVirtualFunction" 출력

 

저작자표시 (새창열림)

'C++ > Basic' 카테고리의 다른 글

[C++] 스마트 포인터  (0) 2024.06.09
함수 오버로딩  (0) 2021.09.17
vector와 list의 차이점  (0) 2021.09.16
[C++] new와 malloc의 차이  (0) 2021.09.15
[C++] 가변인자 템플릿  (0) 2021.08.25
'C++/Basic' 카테고리의 다른 글
  • [C++] 스마트 포인터
  • 함수 오버로딩
  • vector와 list의 차이점
  • [C++] new와 malloc의 차이
chanheess
chanheess
'왜' 그렇게 했는가?에 대한 생각으로 공부 및 작업의 저장관리
  • chanheess
    왜 그렇게 생각했는가?
    chanheess
  • 전체
    오늘
    어제
    • 분류 전체보기
      • Backend Programming
      • Game Programming
        • Unreal
        • DirectX
      • C++
        • Memo
        • Basic
        • Effective Modern
      • Algorithm
        • Memo
        • Baekjoon
        • Programmers
        • HackerRank, LeetCode
      • Data Structure
      • Design Pattern
      • Etc
        • Memo
        • Daily Log
        • Book
  • 최근 글

  • 최근 댓글

  • 태그

    Java
    JPA
    dp
    c++ 기초 플러스
    JWT
    티스토리챌린지
    백준
    SpringSecurity
    spring
    프로그래머스
    위클리 챌린지
    알고리즘
    dfs
    오블완
  • hELLO· Designed By정상우.v4.10.0
chanheess
[C++] 가상함수에 대해
상단으로

티스토리툴바