회사에서 사용하는 레거시 framework가 C++ 기반이다. ACE Framework인데, 이걸 공부하면서 헷갈렸던 / 까먹었던 C++ 내용, 처음 공부했던 내용들에 대해 정리해보고자 한다.
예시와 함께 간단히 알아본다.
class Manager: public Employee 는 Manager는 Employee다.를 의미한다.
- 파생 클래스 객체를 기반 클래스 포인터로 업캐스팅이 가능하다.
- 다만, 다운 캐스팅의 경우 컴파일러가 막는다.
하지만 employee_list[i]->print_info(); 와 같은 코드로, Manager, Employee에 있는 print_info를 동시에 사용하고 싶다면?
- 일반적인 상황이라면 항상 Employee의 print_info만 호출할 수 밖에 없다.
그래서 나온 키워드가 virtual 이다.
class Base {
public:
virtual void what() { std::cout << "기반 클래스의 what()" << std::endl; }
};
class Derived : public Base {
public:
void what() { std::cout << "파생 클래스의 what()" << std::endl; }
};
virtual이 붙으면 컴퓨터는 런타임에 “어 virtual이네?” 하고서 Derived의 what 을 실행하게 된다.
이와 같이 실행 시점에(런타임) 어떤 함수를 호출할지 정해지는 것을 dynamic binding이라고 한다.
무조건 virtual만 붙이면 안될 것 같지만 사실 된다. 컴파일러는 가상 함수가 하나라도 있는 클래스마다 virtual function table(=vtable)을 만든다.
- vtable을 한 단계 더 거쳐서 실제 실행할 함수를 결정 → 크게 오버헤드가 없는듯!
간단히 동작 원리를 보면
- vtable = 클래스마다 하나씩 만들어지는 가상 함수 테이블
- 함수 포인터들의 배열이고, 각 슬롯이 “이 가상함수는 실제로 이 주소의 함수” 정보에 대해 담음
- vptr = 가상 함수 테이블 포인터로, 객체마다 하나씩 들어감
- 자기 클래스의 vtable을 가리키는 포인터

class Parent {
public:
virtual void func1();
virtual void func2();
};
class Child : public Parent {
public:
void func1() override; // 오버라이드
// func2는 물려받음
};
- Parent의 vtable: [0]→
Parent::func1, [1]→Parent::func2 - Child의 vtable: [0]→
Child::func1, [1]→Parent::func2
Parent* p = new Child();
p->func1();
위의 코드가 실행되면
- p가 가리키는 객체의 vptr을 따라감
- → 그 vtable의 0번 슬롯을 읽음
- → 거기 적힌 주소의 함수를 호출
반응형
'금융 > 개발자로 살아남기' 카테고리의 다른 글
| Altibase - 메모리 DB (2) | 2026.07.29 |
|---|---|
| ACE Framework (3) - ACE’ Template (0) | 2026.07.23 |
| ACE Framework (2) - ACE 로그,TCP/IP 소켓 사용법 (0) | 2026.07.22 |
| ACE Framework (1) - ACE 소개 (0) | 2026.07.20 |