CS โ vtable
๐ 04/15 - Vtable (๊ฐ์ ํจ์ ํ ์ด๋ธ) ์ ๋ฆฌ
Notion ์๋ณธ: https://www.notion.so/343f77b24d2f81f78db9cd7ed74a5dc7 ๋ถ๋ชจ: ์๋ฃ โ Cs ๋ฉด์ ์ค๋น โ cs ์ฃผ์
๋ชจ์๋ฉด์ ๋ต๋ณ โ โVtable์ด๋ ๋ฌด์์ธ๊ฐ์?โ
Vtable, ์ฆ ๊ฐ์ ํจ์ ํ ์ด๋ธ์ C++ ์ปดํ์ผ๋ฌ๊ฐ ๋ฐํ์ ๋คํ์ฑ์ ๊ตฌํํ๊ธฐ ์ํด ์๋์ผ๋ก ์์ฑํ๋ ํจ์ ํฌ์ธํฐ ๋ฐฐ์ด์ ๋๋ค. ๊ฐ์ ํจ์(virtual)๊ฐ ํ๋๋ผ๋ ์๋ ํด๋์ค๋ ์ปดํ์ผ ํ์์ vtable์ด ์์ฑ๋๊ณ , ๊ฐ ๊ฐ์ฒด๋ vptr(๊ฐ์ ํฌ์ธํฐ)์ ํตํด ์์ ์ ํด๋์ค vtable์ ๊ฐ๋ฆฌํต๋๋ค.
๊ฐ์ ํจ์๋ฅผ ํธ์ถํ ๋๋ ์ปดํ์ผ ํ์์ ์ด๋ค ํจ์๋ฅผ ํธ์ถํ ์ง ๊ฒฐ์ ํ๋ ๊ฒ์ด ์๋๋ผ, ๋ฐํ์์ vptr โ vtable โ ํจ์ ํฌ์ธํฐ ์์ผ๋ก ํ์ํด ์ค์ ํจ์๋ฅผ ํธ์ถํฉ๋๋ค. ์ด๊ฒ์ด ๋์ ๋์คํจ์น(Dynamic Dispatch)์ ๋๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก vtable ๋๋ถ์ ๋ถ๋ชจ ํ์ ํฌ์ธํฐ๋ก ์์ ๊ฐ์ฒด๋ฅผ ๊ฐ๋ฆฌํค๋๋ผ๋, ํญ์ ์ค์ ํ์ ์ ๋ง๋ ์ฌ๋ฐ๋ฅธ ํจ์๊ฐ ํธ์ถ๋์ด OOP์ ๋ฐํ์ ๋คํ์ฑ์ด ์คํ๋ฉ๋๋ค.
ํค์๋
- vtable (Virtual Function Table) โ ํด๋์ค๋ณ๋ก ์์ฑ๋๋ ๊ฐ์ ํจ์ ํฌ์ธํฐ ๋ฐฐ์ด
- vptr (Virtual Pointer) โ ๊ฐ ๊ฐ์ฒด๊ฐ ๋ด๋ถ์ ์ผ๋ก ๋ณด์ ํ๋ vtable ํฌ์ธํฐ
- ๋์ ๋์คํจ์น (Dynamic Dispatch) โ ๋ฐํ์์ vtable์ ํตํด ์ค์ ํจ์๋ฅผ ์ฐพ์ ํธ์ถํ๋ ๋ฉ์ปค๋์ฆ
- ์ ์ ๋ฐ์ธ๋ฉ (Static Binding) โ ์ปดํ์ผ ํ์์ ํธ์ถ ํจ์๊ฐ ๊ฒฐ์ ๋๋ ๋ฐฉ์ (non-virtual)
- ๋์ ๋ฐ์ธ๋ฉ (Dynamic Binding) โ ๋ฐํ์์ ํธ์ถ ํจ์๊ฐ ๊ฒฐ์ ๋๋ ๋ฐฉ์ (virtual)
- ์์ ๊ฐ์ ํจ์ (Pure Virtual) โ
= 0์ผ๋ก ์ ์ธ, vtable ์ฌ๋กฏ์__cxa_pure_virtualํฌ์ธํฐ ๋ฑ๋ก
Vtable์ด๋?
Vtable์ C++ ์ปดํ์ผ๋ฌ๊ฐ ๊ฐ์ ํจ์๋ฅผ ์ง์ํ๊ธฐ ์ํด ํด๋์ค๋ง๋ค ์๋์ผ๋ก ์์ฑํ๋ ํจ์ ํฌ์ธํฐ ๋ฐฐ์ด์ ๋๋ค.
vtable๊ณผ vptr์ ๊ด๊ณ
| ํญ๋ชฉ | vtable | vptr |
|---|---|---|
| ์กด์ฌ ๋จ์ | ํด๋์ค๋ง๋ค 1๊ฐ | ๊ฐ์ฒด๋ง๋ค 1๊ฐ |
| ์์ฑ ์์ | ์ปดํ์ผ ํ์ | ๋ฐํ์ (์์ฑ์ ํธ์ถ ์) |
| ์ ์ฅ ์์น | ์ฝ๊ธฐ ์ ์ฉ ๋ฉ๋ชจ๋ฆฌ (.rodata) | ๊ฐ์ฒด ๋ฉ๋ชจ๋ฆฌ์ ๋งจ ์ |
| ๋ด์ฉ | ๊ฐ์ ํจ์ ํฌ์ธํฐ ๋ฐฐ์ด | ํด๋น ํด๋์ค์ vtable ์ฃผ์ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Animal {
public:
virtual void Speak() { } // vtable ์ฌ๋กฏ 0
virtual void Move() { } // vtable ์ฌ๋กฏ 1
virtual ~Animal() { } // vtable ์ฌ๋กฏ 2
};
class Dog : public Animal {
public:
void Speak() override { } // ์ฌ๋กฏ 0 โ Dog::Speak ๋ก ๊ต์ฒด
// Move()๋ ์ฌ์ ์ ์ ํจ โ ์ฌ๋กฏ 1 ์ Animal::Move ๊ทธ๋๋ก
};
// Animal vtable: [ Animal::Speak, Animal::Move, Animal::~Animal ]
// Dog vtable: [ Dog::Speak, Animal::Move, Dog::~Dog ]
๋ณต๊ธฐ:
- vtable์ ํด๋์ค๋ง๋ค 1๊ฐ, vptr์ ๊ฐ์ฒด๋ง๋ค 1๊ฐ
virtualํค์๋ ํ๋๋ง ๋ถ์ฌ๋ ์ปดํ์ผ๋ฌ๊ฐ vtable ์๋ ์์ฑ- ์ฌ์ ์(
override)ํ๋ฉด ์์ vtable์ ์ ํจ์ ํฌ์ธํฐ๋ก ๊ต์ฒด - ์ฌ์ ์ ์ ํ ๊ฐ์ ํจ์๋ ๋ถ๋ชจ ํจ์ ํฌ์ธํฐ๋ฅผ ๊ทธ๋๋ก ๋ณต์ฌ
vptr๊ณผ ๋ฉ๋ชจ๋ฆฌ ๊ตฌ์กฐ
vptr(Virtual Pointer)์ ๊ฐ์ฒด๊ฐ ๋ด๋ถ์ ์ผ๋ก ์จ๊ฒจ์ ๋ณด์ ํ๋ ํฌ์ธํฐ๋ก, ํด๋น ๊ฐ์ฒด์ ์ค์ ํด๋์ค vtable์ ๊ฐ๋ฆฌํต๋๋ค. ์์ฑ์๊ฐ ํธ์ถ๋ ๋ vptr์ด ์๋์ผ๋ก ์ด๊ธฐํ๋๋ฉฐ, ๊ฐ์ฒด ๋ฉ๋ชจ๋ฆฌ์ ๋งจ ์์ ์์นํฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Animal {
public:
virtual void Speak() { }
int age; // ๋ฉค๋ฒ ๋ณ์
float weight;
};
// Animal ๊ฐ์ฒด ๋ฉ๋ชจ๋ฆฌ ๊ตฌ์กฐ (64๋นํธ ๊ธฐ์ค):
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// โ vptr (8 bytes) โ Animal์ vtable ์ฃผ์ โ
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
// โ int age (4 bytes) โ
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
// โ float weight (4 bytes) โ
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// sizeof(Animal) = 16 bytes
class Plain { // ๊ฐ์ ํจ์ ์์
public:
int age;
float weight;
};
// sizeof(Plain) = 8 bytes โ vptr ์์!
vptr ์ด๊ธฐํ ๊ณผ์
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Base {
public:
virtual void Foo() { }
};
class Derived : public Base {
public:
void Foo() override { }
};
// ์์ฑ ์์:
// 1. Base ์์ฑ์ ์คํ โ vptr = &Base::vtable
// 2. Derived ์์ฑ์ ์คํ โ vptr = &Derived::vtable
Derived d;
// d.vptr โ Derived::vtable โ [ Derived::Foo ]
Base* p = &d;
p->Foo(); // vptr ๋ฐ๋ผ๊ฐ๋ฉด Derived::vtable โ Derived::Foo
๋ณต๊ธฐ:
- vptr์ ๊ฐ์ฒด ์์ฑ ์ ์์ฑ์๊ฐ ์๋์ผ๋ก ์ด๊ธฐํ (์จ๊ฒจ์ง ๋์)
- ๊ฐ์ ํจ์๊ฐ ์์ผ๋ฉด sizeof(๊ฐ์ฒด)๊ฐ ํฌ์ธํฐ ํฌ๊ธฐ๋งํผ ์ฆ๊ฐ
Base* p = &Derived๊ฐ์ฒดโ vptr์ ์ฌ์ ํ Derived์ vtable ๊ฐ๋ฆฌํด- ์์ฑ์ ์์์ ๊ฐ์ ํจ์ ํธ์ถ ์ฃผ์ โ ์์ง ์์ ์ vtable์ด๋ผ ํ์ ๋ฏธํธ์ถ
๋์ ๋์คํจ์น (Dynamic Dispatch)
๋์ ๋์คํจ์น๋ ๋ฐํ์์ vtable์ ์ฐธ์กฐํด ์ค์ ํธ์ถํ ํจ์๋ฅผ ๊ฒฐ์ ํ๋ ๋ฉ์ปค๋์ฆ์ ๋๋ค.
์ ์ vs ๋์ ๋ฐ์ธ๋ฉ
| ย | ์ ์ ๋ฐ์ธ๋ฉ | ๋์ ๋ฐ์ธ๋ฉ |
|---|---|---|
| ๊ฒฐ์ ์์ | ์ปดํ์ผ ํ์ | ๋ฐํ์ |
| ์กฐ๊ฑด | non-virtual, ๊ฐ ํ์ ํธ์ถ | virtual, ํฌ์ธํฐ/์ฐธ์กฐ ํธ์ถ |
| ์ฑ๋ฅ | ๋น ๋ฆ | vtable ๊ฐ์ ์ฐธ์กฐ ๋น์ฉ |
| ๋คํ์ฑ | ๋ถ๊ฐ | ๊ฐ๋ฅ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Shape {
public:
virtual void Draw() { }
};
class Circle : public Shape {
public:
void Draw() override { }
};
Shape* s = new Circle();
// s->Draw() ๋ด๋ถ ๋์:
// 1. s๊ฐ ๊ฐ๋ฆฌํค๋ ๊ฐ์ฒด์์ vptr ์ฝ๊ธฐ (mov rax, [s])
// 2. vtable์์ Draw ์ฌ๋กฏ ์ฝ๊ธฐ (mov rax, [rax + 0])
// 3. ํจ์ ํฌ์ธํฐ๋ก ๊ฐ์ ํธ์ถ (call rax)
// โ Circle::Draw() ํธ์ถ
Shape s2;
s2.Draw(); // ์ ์ ๋ฐ์ธ๋ฉ โ ์ง์ ํธ์ถ, vtable ์์
๋ณต๊ธฐ:
- ๋์ ๋์คํจ์น = vptr ์ฝ๊ธฐ โ vtable ์ธ๋ฑ์ค ์กฐํ โ ํจ์ ํฌ์ธํฐ ๊ฐ์ ํธ์ถ
- ํฌ์ธํฐ/์ฐธ์กฐ๋ฅผ ํตํ ๊ฐ์ ํจ์ ํธ์ถ๋ง ๋์ ๋์คํจ์น ๋ฐ์
- ๊ฐ ํ์ ์ผ๋ก ํธ์ถํ๋ฉด ์ฌ๋ผ์ด์ฑ(Object Slicing) + ์ ์ ๋ฐ์ธ๋ฉ
finalํค์๋๋ก ์ฌ์ ์ ๋ถ๊ฐ ๋ช ์ โ devirtualize ์ต์ ํ ๊ฐ๋ฅ
์์ ๊ณ์ธต์์์ Vtable
์์ ํด๋์ค๋ ๋ถ๋ชจ vtable์ ๋ณต์ฌํ ๋ค, ์ฌ์ ์(override)ํ ์ฌ๋กฏ๋ง ์ ์ฃผ์๋ก ๊ต์ฒดํฉ๋๋ค. ๋ค์ค ์์ ์์๋ ๊ฐ ๋ถ๋ชจ๋ง๋ค vtable์ด ๋ณ๋๋ก ์กด์ฌํ๋ฉฐ, ๊ฐ์ฒด ๋ด์ vptr๋ ์ฌ๋ฌ ๊ฐ ์๊น๋๋ค.
๋จ์ผ ์์
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A {
public:
virtual void Foo() { } // ์ฌ๋กฏ 0
virtual void Bar() { } // ์ฌ๋กฏ 1
};
class B : public A {
public:
void Foo() override { } // ์ฌ๋กฏ 0 โ B::Foo
};
// A vtable: [ A::Foo, A::Bar ]
// B vtable: [ B::Foo, A::Bar ]
class C : public B {
public:
void Bar() override { }
};
// C vtable: [ B::Foo, C::Bar ]
๋ค์ค ์์
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Base1 { public: virtual void F1() { } };
class Base2 { public: virtual void F2() { } };
class Multi : public Base1, public Base2 {
public:
void F1() override { }
void F2() override { }
};
// Multi ๊ฐ์ฒด ๋ฉ๋ชจ๋ฆฌ:
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// โ vptr1 โ Multi์ Base1์ฉ vtable โ
// โ (Base1 ๋ฉค๋ฒ ๋ณ์๋ค) โ
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
// โ vptr2 โ Multi์ Base2์ฉ vtable โ
// โ (Base2 ๋ฉค๋ฒ ๋ณ์๋ค) โ
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// vptr์ด 2๊ฐ!
๋ณต๊ธฐ:
- ์์ vtable = ๋ถ๋ชจ vtable ๋ณต์ฌ + ์ฌ์ ์ ์ฌ๋กฏ๋ง ๊ต์ฒด
- ๊ฐ์ ํจ์ ์ฌ๋กฏ ์ธ๋ฑ์ค๋ ์์ ๊ณ์ธต ์ ๋ฐ์์ ๋์ผ
- ๋ค์ค ์์ โ vptr ์ฌ๋ฌ ๊ฐ โ ๋ฉ๋ชจ๋ฆฌ & ์ฑ๋ฅ ์ค๋ฒํค๋
- ๋ค์ด์๋ชฌ๋ ์์ + ๊ฐ์ ์์(
virtual public)์ vtable ๊ตฌ์กฐ๊ฐ ๋ ๋ณต์ก
Vtable๊ณผ ์ฑ๋ฅ ๋น์ฉ
Vtable์ ๋ฐํ์ ๋คํ์ฑ์ ๊ฐ๋ฅํ๊ฒ ํ์ง๋ง, ๊ฐ์ ํธ์ถ ๋น์ฉ๊ณผ ์ธ๋ผ์ด๋ ๋ถ๊ฐ๋ผ๋ ์ฑ๋ฅ ํธ๋ ์ด๋์คํ๊ฐ ์์ต๋๋ค.
๋น์ฉ ๋ถ์
| ๋น์ฉ ํญ๋ชฉ | ์ค๋ช | ์ํฅ๋ |
|---|---|---|
| ๋ฉ๋ชจ๋ฆฌ ์ค๋ฒํค๋ | ๊ฐ์ฒด๋น vptr 1๊ฐ (8 bytes) | ๋ฎ์ |
| ๊ฐ์ ํธ์ถ ๋น์ฉ | vptr โ vtable โ ํจ์ ํฌ์ธํฐ 2ํ ์ญ์ฐธ์กฐ | ์ค๊ฐ |
| ์ธ๋ผ์ด๋ ๋ถ๊ฐ | ์ปดํ์ผ ํ์์ ํจ์ ํน์ ๋ถ๊ฐ | ๋์ |
| ์บ์ ๋ฏธ์ค | vtable ํฌ์ธํฐ ์ญ์ฐธ์กฐ ์ | ์ค๊ฐ~๋์ |
์ฑ๋ฅ ๊ฐ์ ๋ฐฉ๋ฒ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 1. final ํค์๋๋ก devirtualization ์ ๋
class Circle final : public Shape {
public:
void Draw() override { } // ๋ ์ด์ ์ฌ์ ์ ๋ถ๊ฐ
};
Circle* c = new Circle();
c->Draw(); // devirtualized โ vtable ์ฐธ์กฐ ์์ด ์ง์ ํธ์ถ!
// 2. ๊ฐ ํ์
์ผ๋ก ์ง์ ํธ์ถ
Circle c2;
c2.Draw(); // ์ ์ ๋ฐ์ธ๋ฉ
// 3. CRTP โ ์ปดํ์ผ ํ์ ๋คํ์ฑ (์ ๋ก ์ค๋ฒํค๋)
template<typename Derived>
class ShapeBase {
public:
void Draw() { static_cast<Derived*>(this)->DrawImpl(); }
};
class SquareCRTP : public ShapeBase<SquareCRTP> {
public:
void DrawImpl() { }
};
๋ณต๊ธฐ:
- vtable ๋น์ฉ = ๊ฐ์ ํธ์ถ(2ํ) + ์ธ๋ผ์ด๋ ๋ถ๊ฐ + ์บ์ ๋ฏธ์ค
finalโ devirtualize ์ต์ ํ- ์ฑ๋ฅ์ด ์ค์ํ Hot Path๋ CRTP๋ก ์ปดํ์ผ ํ์ ๋คํ์ฑ
- ๋๋ถ๋ถ์ ๊ฒฝ์ฐ vtable ๋น์ฉ์ ์ธก์ ๊ฐ๋ฅํ ๋ณ๋ชฉ์ด ์๋ โ ๋จผ์ ํ๋กํ์ผ๋ง
์ธ๋ฆฌ์ผ ์์ง๊ณผ Vtable
์ธ๋ฆฌ์ผ ์์ง์ AActor, UObject ๋ฑ ํต์ฌ ํด๋์ค๋ ๊ฐ์ ํจ์๋ฅผ ๊ด๋ฒ์ํ๊ฒ ํ์ฉํฉ๋๋ค.
์ฃผ์ ๊ฐ์ ํจ์
| ํจ์ | ์ ์ธ ์์น | vtable ์ญํ |
|---|---|---|
| BeginPlay() | AActor | ๊ฐ Actor ํ์ ์ ์ด๊ธฐํ ๋์ ๋์คํจ์น |
| Tick(float DeltaTime) | AActor | ๋งค ํ๋ ์ ํ์ ๋ณ ์ ๋ฐ์ดํธ ๋์ ๋์คํจ์น |
| TakeDamage() | AActor | ๋ฐ๋ฏธ์ง ์ฒ๋ฆฌ ๋ก์ง ๋คํ์ฑ |
| PostInitializeComponents() | AActor | ์ปดํฌ๋ํธ ์ด๊ธฐํ ํ ์ปค์คํ ๋ก์ง |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class AActor : public UObject {
public:
virtual void BeginPlay();
virtual void Tick(float DeltaTime);
virtual float TakeDamage(...);
};
UCLASS()
class AMyCharacter : public ACharacter {
GENERATED_BODY()
public:
virtual void BeginPlay() override {
Super::BeginPlay(); // ๋ถ๋ชจ vtable ์ฌ๋กฏ ์ง์ ํธ์ถ (์ ์ ๋ฐ์ธ๋ฉ)
}
virtual void Tick(float DeltaTime) override {
Super::Tick(DeltaTime);
}
};
// ์์ง ๋ด๋ถ:
// TArray<AActor*> Actors;
// for (AActor* Actor : Actors)
// Actor->Tick(DeltaTime); // vtable โ ๊ฐ ํ์
์ ์ฌ๋ฐ๋ฅธ Tick ํธ์ถ
๋ณต๊ธฐ:
- ์ธ๋ฆฌ์ผ์
BeginPlay(),Tick()= ๊ฐ์ ํจ์ โ ๋์ ๋์คํจ์น Super::BeginPlay()= ๋ถ๋ชจ vtable ์ฌ๋กฏ ์ง์ ์ง์ ํธ์ถ (์ ์ ๋ฐ์ธ๋ฉ)TArray<AActor*>๋ก ๋ค์ํ ํ์ ๊ด๋ฆฌ โ vtable์ด ์ฌ๋ฐ๋ฅธ ํจ์ ๋ณด์ฅ- ์ฑ๋ฅ ๋ฏผ๊ฐ ์ฝ๋๋
FORCEINLINE, ๋น๊ฐ์ ํจ์ ๋ถ๋ฆฌ, ECS ํจํด ๊ณ ๋ ค