ํฌ์ŠคํŠธ

CS โ€” vtable

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์˜ ๊ด€๊ณ„

ํ•ญ๋ชฉvtablevptr
์กด์žฌ ๋‹จ์œ„ํด๋ž˜์Šค๋งˆ๋‹ค 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 ํŒจํ„ด ๊ณ ๋ ค
์ด ๊ธฐ์‚ฌ๋Š” ์ €์ž‘๊ถŒ์ž์˜ CC BY 4.0 ๋ผ์ด์„ผ์Šค๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

ยฉ GoldBoll. ์ผ๋ถ€ ๊ถŒ๋ฆฌ ๋ณด์œ 

Powered by Jekyll with Chirpy theme

์ธ๊ธฐ ํƒœ๊ทธ