ํฌ์ŠคํŠธ

CS โ€” vtable deepdive

CS โ€” vtable deepdive

๐Ÿ“• 04/23 โ€” vtable ์‹ฌํ™” (๋ชจ์˜๋ฉด์ ‘ ๋ฏธ๋‹ต๋ณ€ ๋ณต๊ธฐ)

์˜ค๋Š˜ ๋ชจ์˜๋ฉด์ ‘์—์„œ ๋ง‰ํžŒ ์งˆ๋ฌธ๋“ค์„ ์ •๋ฆฌํ•œ ์‹ฌํ™” ํŒŒ์ผ ๊ธฐ๋ณธ ๊ฐœ๋…์€ โ†’ 05_vtable.md


๋ชฉ์ฐจ

  1. vtable ๋‹จ์ 
  2. vtable์€ ๊ฐ์ฒด๋งˆ๋‹ค? ํด๋ž˜์Šค๋งˆ๋‹ค?
  3. ์ˆœ์ˆ˜ ๊ฐ€์ƒ ํ•จ์ˆ˜์™€ _purecall
  4. ์ž์‹ override ํ•จ์ˆ˜๊ฐ€ vtable ๋ฐฐ์—ด ์•ˆ์—์„œ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋‚˜
  5. override๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ vtable ์Šฌ๋กฏ์€?
  6. virtual ์„ ์–ธ ์‹œ vtable๊ณผ ํ•จ๊ป˜ ์ƒ์„ฑ๋˜๋Š” RTTI
  7. ์–ธ๋ฆฌ์–ผ์—์„œ SetPurecallHandler

1. vtable ๋‹จ์ 

Q: โ€œvtable์„ ์‚ฌ์šฉํ•˜๋ฉด ์–ด๋–ค ๋‹จ์ ์ด ์žˆ๋‚˜์š”?โ€

๋‹จ์  4๊ฐ€์ง€

๋‹จ์ ์„ค๋ช…ํฌ๊ธฐ
๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ์ฒด๋งˆ๋‹ค vptr 8๋ฐ”์ดํŠธ ์ถ”๊ฐ€๋‚ฎ์Œ
๊ฐ„์ ‘ ํ˜ธ์ถœ ๋น„์šฉvptr โ†’ vtable โ†’ ํ•จ์ˆ˜ํฌ์ธํ„ฐ, ์—ญ์ฐธ์กฐ 2๋ฒˆ์ค‘๊ฐ„
์ธ๋ผ์ด๋‹ ๋ถˆ๊ฐ€์ปดํŒŒ์ผ ํƒ€์ž„์— ์–ด๋–ค ํ•จ์ˆ˜ ํ˜ธ์ถœ๋ ์ง€ ๋ชจ๋ฆ„ โ†’ ์ธ๋ผ์ธ ์ตœ์ ํ™” ์ฐจ๋‹จ๋†’์Œ
์บ์‹œ ๋ฏธ์Šคvtable์ด .rodata์— ์žˆ์–ด ์บ์‹œ ์›Œ๋ฐ์ด ์•ˆ ๋œ ๊ฒฝ์šฐ ์บ์‹œ ๋ฏธ์Šค์ค‘๊ฐ„~๋†’์Œ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ์ธ๋ผ์ด๋‹ ๋ถˆ๊ฐ€ ์˜ˆ์‹œ
class Shape {
public:
    virtual void Draw() { }  // ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ธ๋ผ์ธ ๋ถˆ๊ฐ€
};

// CRTP๋กœ ์ธ๋ผ์ด๋‹ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋ณ€ํ™˜
template<typename Derived>
class ShapeBase {
public:
    void Draw() {
        static_cast<Derived*>(this)->DrawImpl();  // ์ปดํŒŒ์ผ ํƒ€์ž„ ๊ฒฐ์ • โ†’ ์ธ๋ผ์ธ ๊ฐ€๋Šฅ
    }
};

์ถ”๊ฐ€ ๋‹จ์ : ์ƒ์„ฑ์ž/์†Œ๋ฉธ์ž ๋‚ด๋ถ€ ๊ฐ€์ƒ ํ•จ์ˆ˜ ํ˜ธ์ถœ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Base {
public:
    Base() { Foo(); }       // ์œ„ํ—˜! ์ด ์‹œ์ ์˜ vptr์€ Base::vtable
    virtual void Foo() { std::cout << "Base::Foo\n"; }
};

class Derived : public Base {
public:
    void Foo() override { std::cout << "Derived::Foo\n"; }
};

Derived d;  // "Base::Foo" ์ถœ๋ ฅ โ€” Derived::Foo๊ฐ€ ์•„๋‹˜!
// ์ด์œ : Base ์ƒ์„ฑ์ž ์‹คํ–‰ ์‹œ์ ์—” vptr = &Base::vtable
//       Derived ์ƒ์„ฑ์ž ์‹คํ–‰ ํ›„์—์•ผ vptr = &Derived::vtable

30์ดˆ ๋‹ต๋ณ€:
vtable์˜ ๋‹จ์ ์€ ์„ธ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ์ฒซ์งธ, ๊ฐ„์ ‘ ํ˜ธ์ถœ ๋น„์šฉ โ€” vptr์—์„œ vtable, vtable์—์„œ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๊นŒ์ง€ ์—ญ์ฐธ์กฐ๊ฐ€ 2๋ฒˆ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‘˜์งธ, ์ปดํŒŒ์ผ ํƒ€์ž„์— ํ˜ธ์ถœ ํ•จ์ˆ˜๋ฅผ ํŠน์ •ํ•  ์ˆ˜ ์—†์–ด ์ธ๋ผ์ธ ์ตœ์ ํ™”๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์…‹์งธ, vtable ํฌ์ธํ„ฐ ์—ญ์ฐธ์กฐ ์‹œ ์บ์‹œ ๋ฏธ์Šค๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•œ Hot Path๋ผ๋ฉด final ํ‚ค์›Œ๋“œ๋กœ devirtualizeํ•˜๊ฑฐ๋‚˜ CRTP๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.


2. vtable์€ ๊ฐ์ฒด๋งˆ๋‹ค? ํด๋ž˜์Šค๋งˆ๋‹ค?

Q: โ€œvtable์€ ๊ฐ์ฒด๋งˆ๋‹ค ์ƒ์„ฑ๋˜๋‚˜์š”, ํด๋ž˜์Šค๋งˆ๋‹ค ์ƒ์„ฑ๋˜๋‚˜์š”?โ€

ํ•ต์‹ฌ ๊ตฌ๋ถ„

1
2
vtable  โ†’ ํด๋ž˜์Šค๋งˆ๋‹ค 1๊ฐœ  (์ปดํŒŒ์ผ ํƒ€์ž„ ์ƒ์„ฑ, .rodata ์ฝ๊ธฐ ์ „์šฉ ์˜์—ญ)
vptr    โ†’ ๊ฐ์ฒด๋งˆ๋‹ค  1๊ฐœ  (๋Ÿฐํƒ€์ž„, ์ƒ์„ฑ์ž ํ˜ธ์ถœ ์‹œ ์ดˆ๊ธฐํ™”)
1
2
3
4
5
6
7
8
9
10
class Animal {
public:
    virtual void Speak() { }
};

Animal a1, a2, a3;

// vtable: Animal::vtable ๋”ฑ 1๊ฐœ โ€” ์„ธ ๊ฐ์ฒด๊ฐ€ ๊ณต์œ 
// vptr:   a1.vptr, a2.vptr, a3.vptr ๊ฐ๊ฐ ์กด์žฌ
//         (๋ชจ๋‘ ๊ฐ™์€ Animal::vtable์„ ๊ฐ€๋ฆฌํ‚ด)
1
2
3
4
5
6
7
8
๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์กฐ:

a1: [ vptr โ†’ Animal::vtable ] [ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜ ]
a2: [ vptr โ†’ Animal::vtable ] [ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜ ]   โ† ๊ฐ™์€ vtable ๊ฐ€๋ฆฌํ‚ด
a3: [ vptr โ†’ Animal::vtable ] [ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜ ]

Animal::vtable (์ฝ๊ธฐ ์ „์šฉ, .rodata):
  [ &Animal::Speak, &Animal::~Animal ]

์™œ ํด๋ž˜์Šค๋‹น 1๊ฐœ์ธ๊ฐ€?

vtable์˜ ๋‚ด์šฉ(ํ•จ์ˆ˜ ํฌ์ธํ„ฐ ๋ฐฐ์—ด)์€ ๋ชจ๋“  ๊ฐ์ฒด์—์„œ ๋™์ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ณต์œ ํ•ด๋„ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ์ฒด๋ณ„๋กœ ๋‹ฌ๋ผ์ง€๋Š” ๊ฒƒ์€ vptr์ด ๊ฐ€๋ฆฌํ‚ค๋Š” ๋Œ€์ƒ(์–ด๋–ค ํด๋ž˜์Šค์˜ vtable์ธ์ง€)์ด์ง€, vtable ์ž์ฒด์˜ ๋‚ด์šฉ์ด ์•„๋‹™๋‹ˆ๋‹ค.

30์ดˆ ๋‹ต๋ณ€:
vtable์€ ํด๋ž˜์Šค๋งˆ๋‹ค 1๊ฐœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ ํƒ€์ž„์— ๋งŒ๋“ค์–ด์ ธ ์ฝ๊ธฐ ์ „์šฉ ๋ฉ”๋ชจ๋ฆฌ(.rodata)์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด vptr์€ ๊ฐ์ฒด๋งˆ๋‹ค 1๊ฐœ์”ฉ ์กด์žฌํ•˜๋ฉฐ, ์ƒ์„ฑ์ž ํ˜ธ์ถœ ์‹œ ๋Ÿฐํƒ€์ž„์— ํ•ด๋‹น ํด๋ž˜์Šค์˜ vtable ์ฃผ์†Œ๋กœ ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ํด๋ž˜์Šค์˜ ๋ชจ๋“  ๊ฐ์ฒด๋Š” ๋™์ผํ•œ vtable์„ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.


3. ์ˆœ์ˆ˜ ๊ฐ€์ƒ ํ•จ์ˆ˜์™€ _purecall

Q: โ€œ์ˆœ์ˆ˜ ๊ฐ€์ƒ ํ•จ์ˆ˜๋Š” vtable์—์„œ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌ๋˜๋‚˜์š”? ํ˜ธ์ถœํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ฉ๋‹ˆ๊นŒ?โ€

์ˆœ์ˆ˜ ๊ฐ€์ƒ ํ•จ์ˆ˜ ์„ ์–ธ

1
2
3
4
5
class IShape {
public:
    virtual void Draw() = 0;    // ์ˆœ์ˆ˜ ๊ฐ€์ƒ ํ•จ์ˆ˜
    virtual ~IShape() = default;
};
  • = 0 ์„ ์–ธ โ†’ ์ด ํด๋ž˜์Šค๋Š” ์ถ”์ƒ ํด๋ž˜์Šค(instantiation ๋ถˆ๊ฐ€)
  • vtable ์Šฌ๋กฏ์—๋Š” __cxa_pure_virtual (GCC/Clang) ๋˜๋Š” _purecall (MSVC) ํ•จ์ˆ˜ ํฌ์ธํ„ฐ ๋“ฑ๋ก

_purecall ๋ฐœ์ƒ ์‹œ๋‚˜๋ฆฌ์˜ค

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Base {
public:
    virtual void Foo() = 0;
    Base() {
        Foo();  // UB! ์ƒ์„ฑ์ž ์•ˆ์—์„œ ์ˆœ์ˆ˜ ๊ฐ€์ƒ ํ•จ์ˆ˜ ํ˜ธ์ถœ
                // vptr์ด ์•„์ง Base::vtable โ†’ _purecall ํ˜ธ์ถœ โ†’ ํ”„๋กœ๊ทธ๋žจ ์ข…๋ฃŒ
    }
};

class Derived : public Base {
public:
    void Foo() override { }
};

Derived d;  // Base() ์ƒ์„ฑ์ž์—์„œ _purecall ํ˜ธ์ถœ๋จ

_purecall ๋™์ž‘:

  1. ์ˆœ์ˆ˜ ๊ฐ€์ƒ ํ•จ์ˆ˜ ํ˜ธ์ถœ ๊ฐ์ง€
  2. MSVC CRT์˜ _purecall() ํ•จ์ˆ˜ ์‹คํ–‰
  3. ๊ธฐ๋ณธ ๋™์ž‘: abort() โ†’ ํ”„๋กœ๊ทธ๋žจ ์ฆ‰์‹œ ์ข…๋ฃŒ

_set_purecall_handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdlib.h>

// ์ปค์Šคํ…€ ํ•ธ๋“ค๋Ÿฌ โ€” ๋””๋ฒ„๊น…/๋กœ๊น… ๋ชฉ์ 
void MyPureCallHandler() {
    // ์Šคํƒ ํŠธ๋ ˆ์ด์Šค ์ถœ๋ ฅ, ํฌ๋ž˜์‹œ ๋ฆฌํฌํŠธ ์ „์†ก ๋“ฑ
    std::cerr << "[FATAL] ์ˆœ์ˆ˜ ๊ฐ€์ƒ ํ•จ์ˆ˜ ํ˜ธ์ถœ ๊ฐ์ง€!\n";
    // ๋ฐ˜ํ™˜ํ•˜์ง€ ๋ง ๊ฒƒ โ€” ๋ฐ˜ํ™˜ํ•˜๋ฉด UB
    abort();
}

int main() {
    _set_purecall_handler(MyPureCallHandler);
    // ์ดํ›„ _purecall ๋ฐœ์ƒ ์‹œ MyPureCallHandler ํ˜ธ์ถœ
}
  • _set_purecall_handler๋กœ ์ปค์Šคํ…€ ํ•ธ๋“ค๋Ÿฌ ๊ต์ฒด ๊ฐ€๋Šฅ
  • ํ•ธ๋“ค๋Ÿฌ๋Š” ๋ฐ˜๋“œ์‹œ ์ข…๋ฃŒํ•ด์•ผ ํ•จ (๋ฐ˜ํ™˜ ์‹œ Undefined Behavior)
  • ๋””๋ฒ„๊ทธ ๋นŒ๋“œ์—์„œ ์ฝœ์Šคํƒ ๋คํ”„, ํฌ๋ž˜์‹œ ๋ฆฌํฌํ„ฐ ์—ฐ๋™์— ํ™œ์šฉ

4. ์ž์‹ override ํ•จ์ˆ˜๊ฐ€ vtable ๋ฐฐ์—ด ์•ˆ์—์„œ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋‚˜

Q: โ€œ์ž์‹ ํด๋ž˜์Šค์—์„œ overrideํ•œ ํ•จ์ˆ˜๊ฐ€ vtable ๋ฐฐ์—ด ์•ˆ์—์„œ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๊นŒ?โ€

์Šฌ๋กฏ ๊ต์ฒด ๋ฉ”์ปค๋‹ˆ์ฆ˜

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Animal {
public:
    virtual void Speak() { }   // ์Šฌ๋กฏ [0]
    virtual void Move()  { }   // ์Šฌ๋กฏ [1]
    virtual ~Animal()    { }   // ์Šฌ๋กฏ [2]
};

class Dog : public Animal {
public:
    void Speak() override { }  // ์Šฌ๋กฏ [0] ๊ต์ฒด
    // Move()๋Š” override ์•ˆ ํ•จ
};

// Animal vtable: [ &Animal::Speak, &Animal::Move, &Animal::~Animal ]
// Dog    vtable: [ &Dog::Speak,    &Animal::Move, &Dog::~Dog       ]
//                  ^^^^^^^^^^^^โ€” ์Šฌ๋กฏ [0]๋งŒ ๊ต์ฒด๋จ

๋Ÿฐํƒ€์ž„ ํ˜ธ์ถœ ํ๋ฆ„

1
2
3
4
5
6
7
8
9
10
11
12
Animal* a = new Dog();
a->Speak();

1. a๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฐ์ฒด์˜ vptr ์ฝ๊ธฐ     โ†’ Dog::vtable ์ฃผ์†Œ
2. Dog::vtable[0] ์ฝ๊ธฐ              โ†’ &Dog::Speak
3. Dog::Speak() ํ˜ธ์ถœ                โ† ์˜ฌ๋ฐ”๋ฅธ ์ž์‹ ํ•จ์ˆ˜!

a->Move();

1. a์˜ vptr ์ฝ๊ธฐ                    โ†’ Dog::vtable ์ฃผ์†Œ
2. Dog::vtable[1] ์ฝ๊ธฐ              โ†’ &Animal::Move  (๋ณต์‚ฌ๋œ ํฌ์ธํ„ฐ)
3. Animal::Move() ํ˜ธ์ถœ

ํ•ต์‹ฌ: ์Šฌ๋กฏ ์ธ๋ฑ์Šค๋Š” ์ƒ์† ๊ณ„์ธต ์ „๋ฐ˜์—์„œ ๋™์ผํ•˜๊ฒŒ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ๋Ÿฌ๋Š” virtual void Speak()๊ฐ€ ํ•ญ์ƒ ์Šฌ๋กฏ [0]์ž„์„ ์•Œ๊ณ  ์žˆ์œผ๋ฏ€๋กœ, ํฌ์ธํ„ฐ ํƒ€์ž…์ด ๋ฌด์—‡์ด๋“  ๊ฐ™์€ ์˜คํ”„์…‹์œผ๋กœ ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.


5. override๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ vtable ์Šฌ๋กฏ์€?

Q: โ€œ์ž์‹ ํด๋ž˜์Šค์—์„œ overrideํ•˜์ง€ ์•Š์€ ๊ฐ€์ƒ ํ•จ์ˆ˜๋Š” ์–ด๋–ป๊ฒŒ ๋ฉ๋‹ˆ๊นŒ?โ€

๋ถ€๋ชจ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Animal {
public:
    virtual void Speak() { std::cout << "...\n"; }
    virtual void Move()  { std::cout << "์ด๋™\n"; }
};

class Dog : public Animal {
public:
    void Speak() override { std::cout << "๋ฉ๋ฉ\n"; }
    // Move()๋Š” override ์•ˆ ํ•จ
};

Animal* a = new Dog();
a->Move();   // Animal::Move() ํ˜ธ์ถœ
             // Dog vtable์˜ [1] = &Animal::Move (๋ณต์‚ฌ๋œ ํฌ์ธํ„ฐ์ด๋ฏ€๋กœ)
1
2
3
4
5
6
7
Dog vtable ์ƒ์„ฑ ๊ณผ์ •:
1. Animal vtable์„ ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌ:
   [ &Animal::Speak, &Animal::Move, &Animal::~Animal ]

2. override๋œ ์Šฌ๋กฏ๋งŒ ๊ต์ฒด:
   [ &Dog::Speak,    &Animal::Move, &Dog::~Dog       ]
                      ^^^^^^^^^^^^โ€” ๊ต์ฒด ์—†์Œ, ๋ถ€๋ชจ ๊ฒƒ ๊ทธ๋Œ€๋กœ

๊ฒฐ๊ณผ: Dog ๊ฐ์ฒด์—์„œ Move()๋ฅผ ํ˜ธ์ถœํ•ด๋„ Animal::Move()๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์Šฌ๋กฏ์— ๋ถ€๋ชจ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๊ฐ€ ๋ณต์‚ฌ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

30์ดˆ ๋‹ต๋ณ€:
์ž์‹ ํด๋ž˜์Šค๊ฐ€ ๊ฐ€์ƒ ํ•จ์ˆ˜๋ฅผ overrideํ•˜์ง€ ์•Š์œผ๋ฉด, ์ž์‹์˜ vtable์—๋Š” ๋ถ€๋ชจ์˜ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ž์‹ ๊ฐ์ฒด๋ฅผ ๋ถ€๋ชจ ํฌ์ธํ„ฐ๋กœ ๊ฐ€๋ฆฌํ‚ค๊ณ  ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด๋„ ๋ถ€๋ชจ์˜ ๊ตฌํ˜„์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.


6. virtual ์„ ์–ธ ์‹œ vtable๊ณผ ํ•จ๊ป˜ ์ƒ์„ฑ๋˜๋Š” RTTI

Q: โ€œvirtual ํ‚ค์›Œ๋“œ๋ฅผ ์“ฐ๋ฉด vtable ์ƒ์„ฑ๊ณผ ํ•จ๊ป˜ RTTI๋„ ์ƒ์„ฑ๋˜๋‚˜์š”?โ€

vtable์˜ ์‹ค์ œ ๊ตฌ์กฐ (์ˆจ๊ฒจ์ง„ ํ—ค๋” ํฌํ•จ)

1
2
3
4
5
6
7
8
9
10
vtable ๋ฉ”๋ชจ๋ฆฌ ๋ ˆ์ด์•„์›ƒ (GCC/Clang ABI ๊ธฐ์ค€):

[ type_info*   ]  โ† RTTI ์ •๋ณด ํฌ์ธํ„ฐ (vtable[-1])
[ offset_to_top]  โ† ๋‹ค์ค‘ ์ƒ์†์šฉ ์˜คํ”„์…‹ (vtable[-2])
[ Func1*       ]  โ† ๊ฐ€์ƒ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ [0]
[ Func2*       ]  โ† ๊ฐ€์ƒ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ [1]
[ ...          ]

vptr๋Š” Func1* ์œ„์น˜๋ฅผ ๊ฐ€๋ฆฌํ‚ด
RTTI๋Š” vptr[-1]๋กœ ์ ‘๊ทผ
1
2
3
4
5
6
7
8
9
10
11
12
class Animal {
public:
    virtual void Speak() { }  // virtual ํ•˜๋‚˜๋งŒ ์žˆ์–ด๋„ RTTI ์ž๋™ ์ƒ์„ฑ
};

Animal* a = new Dog();

// typeid ์‚ฌ์šฉ โ†’ ๋‚ด๋ถ€์ ์œผ๋กœ vptr[-1]์˜ type_info ์ ‘๊ทผ
std::cout << typeid(*a).name();  // "Dog" ์ถœ๋ ฅ

// dynamic_cast๋„ RTTI ์‚ฌ์šฉ
Dog* d = dynamic_cast<Dog*>(a);  // vtable์˜ type_info๋กœ ํƒ€์ž… ํ™•์ธ

RTTI ์ƒ์„ฑ ์กฐ๊ฑด

์กฐ๊ฑดRTTI ์ƒ์„ฑ ์—ฌ๋ถ€
virtual ํ•จ์ˆ˜ ์—†์Œ์ƒ์„ฑ ์•ˆ ๋จ
virtual ํ•จ์ˆ˜ 1๊ฐœ ์ด์ƒ์ž๋™ ์ƒ์„ฑ
-fno-rtti ์ปดํŒŒ์ผ ํ”Œ๋ž˜๊ทธ๋ช…์‹œ์ ์œผ๋กœ ๋น„ํ™œ์„ฑํ™”
1
2
3
4
5
6
7
8
9
10
class Plain {          // virtual ์—†์Œ โ†’ RTTI ์—†์Œ
    int x;
};

class Base {           // virtual ์žˆ์Œ โ†’ RTTI ์ž๋™ ์ƒ์„ฑ
    virtual void Foo() { }
};

// typeid(Plain{}) โ€” ์ •์  RTTI (์ปดํŒŒ์ผ ํƒ€์ž„, vtable ๋ถˆํ•„์š”)
// typeid(*base_ptr) โ€” ๋™์  RTTI (๋Ÿฐํƒ€์ž„, vtable ํ•„์š”)

30์ดˆ ๋‹ต๋ณ€:
virtual ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ปดํŒŒ์ผ๋Ÿฌ๋Š” vtable์„ ์ƒ์„ฑํ•˜๋ฉด์„œ type_info ํฌ์ธํ„ฐ๋„ ํ•จ๊ป˜ vtable ํ—ค๋”์— ์‹ฌ์Šต๋‹ˆ๋‹ค. typeid(*ptr)๋‚˜ dynamic_cast๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด vptr์„ ํ†ตํ•ด ์ด type_info์— ์ ‘๊ทผํ•˜์—ฌ ๋Ÿฐํƒ€์ž„ ํƒ€์ž…์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰ RTTI๋Š” vtable์ด ์žˆ์–ด์•ผ๋งŒ ๋™์ž‘ํ•˜๋Š” ๋™์  ํƒ€์ž… ์ •๋ณด ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค.


7. ์–ธ๋ฆฌ์–ผ์—์„œ SetPurecallHandler

Q: โ€œ์–ธ๋ฆฌ์–ผ ์—”์ง„์—์„œ SetPurecallHandler๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?โ€

์–ธ๋ฆฌ์–ผ์€ ์—”์ง„ ์ดˆ๊ธฐํ™” ์‹œ _set_purecall_handler๋ฅผ ์ด์šฉํ•ด ์ปค์Šคํ…€ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. ์ˆœ์ˆ˜ ๊ฐ€์ƒ ํ•จ์ˆ˜ ํ˜ธ์ถœ(๊ฐœ๋ฐœ ์ค‘ ์‹ค์ˆ˜)์„ ์—”์ง„ ํฌ๋ž˜์‹œ ๋ฆฌํฌํ„ฐ์™€ ์—ฐ๋™ํ•˜๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
// ์–ธ๋ฆฌ์–ผ ์—”์ง„ ๋‚ด๋ถ€ (Windows ํ”Œ๋žซํผ)
// UnrealEngine/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformCrashContext.cpp

static void PureCallHandler() {
    // 1. ์ฝœ์Šคํƒ ์บก์ฒ˜
    // 2. ํฌ๋ž˜์‹œ ๋ฆฌํฌํ„ฐ๋กœ ์ „์†ก
    // 3. ์—๋””ํ„ฐ๋ฉด ๋ฉ”์‹œ์ง€ ๋ฐ•์Šค ํ‘œ์‹œ
    UE_LOG(LogCore, Fatal, TEXT("Pure virtual function called"));
    // โ†’ FPlatformMisc::RaiseException ํ˜ธ์ถœ โ†’ ํฌ๋ž˜์‹œ ๋‹ค์ด์–ผ๋กœ๊ทธ
}

// ์—”์ง„ ์‹œ์ž‘ ์‹œ:
_set_purecall_handler(PureCallHandler);

์‹ค์ œ๋กœ ๋งˆ์ฃผ์น˜๋Š” ์ƒํ™ฉ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ์–ธ๋ฆฌ์–ผ์—์„œ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ์ผ€์ด์Šค:
// ์ƒ์„ฑ์ž์—์„œ ์ˆœ์ˆ˜ ๊ฐ€์ƒ ํ•จ์ˆ˜ ํ˜ธ์ถœ (vtable์ด ์™„์„ฑ๋˜๊ธฐ ์ „)

UCLASS()
class AMyActor : public AActor {
    GENERATED_BODY()
public:
    AMyActor() {
        InitData();  // ๋งŒ์•ฝ InitData๊ฐ€ ์ˆœ์ˆ˜ ๊ฐ€์ƒ์ด๋ผ๋ฉด โ€” purecall!
    }
    virtual void InitData() = 0;  // ์ˆœ์ˆ˜ ๊ฐ€์ƒ
};

// ์—๋Ÿฌ ๋ฉ”์‹œ์ง€:
// "Pure virtual function called" + ํฌ๋ž˜์‹œ ๋ฆฌํฌํ„ฐ ํŒ์—…

์‹ค๋ฌด ํŒ: ์–ธ๋ฆฌ์–ผ์—์„œ ์ด ์˜ค๋ฅ˜๋ฅผ ๋ณด๋ฉด ์ƒ์„ฑ์ž ๋‚ด ๊ฐ€์ƒ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์—ฌ๋ถ€ ๋จผ์ € ํ™•์ธ.
ํ•ด๊ฒฐ์ฑ…: ์ƒ์„ฑ์ž ๋Œ€์‹  BeginPlay()๋‚˜ PostInitializeComponents()์—์„œ ์ดˆ๊ธฐํ™”.


ํ•ต์‹ฌ ์š”์•ฝ (30์ดˆ ๋‹ต๋ณ€ ์นด๋“œ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
vtable ๋‹จ์ :
  ๊ฐ„์ ‘ ํ˜ธ์ถœ(์—ญ์ฐธ์กฐ 2๋ฒˆ) + ์ธ๋ผ์ด๋‹ ๋ถˆ๊ฐ€ + ์บ์‹œ ๋ฏธ์Šค
  โ†’ Hot Path: final(devirtualize) ๋˜๋Š” CRTP

vtable vs vptr:
  vtable = ํด๋ž˜์Šค๋‹น 1๊ฐœ (์ปดํŒŒ์ผ ํƒ€์ž„, .rodata)
  vptr   = ๊ฐ์ฒด๋‹น 1๊ฐœ  (๋Ÿฐํƒ€์ž„, ์ƒ์„ฑ์ž์—์„œ ์ดˆ๊ธฐํ™”)

์ˆœ์ˆ˜ ๊ฐ€์ƒ + purecall:
  = 0 ์„ ์–ธ โ†’ vtable ์Šฌ๋กฏ์— _purecall ๋“ฑ๋ก
  ํ˜ธ์ถœ ์‹œ ํ”„๋กœ๊ทธ๋žจ ์ข…๋ฃŒ (_set_purecall_handler๋กœ ์ปค์Šคํ…€ ๊ฐ€๋Šฅ)

override ์—†๋Š” ์Šฌ๋กฏ:
  ๋ถ€๋ชจ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌ โ†’ ๋ถ€๋ชจ ๊ตฌํ˜„ ์‹คํ–‰

vtable + RTTI:
  virtual ์žˆ์œผ๋ฉด type_info* ๊ฐ€ vtable ํ—ค๋”์— ์ž๋™ ์ƒ์„ฑ
  typeid / dynamic_cast = vtable[-1] ์ ‘๊ทผ

์ฐธ๊ณ 

์ด ๊ธฐ์‚ฌ๋Š” ์ €์ž‘๊ถŒ์ž์˜ CC BY 4.0 ๋ผ์ด์„ผ์Šค๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

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

Powered by Jekyll with Chirpy theme

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