ํฌ์ŠคํŠธ

CS โ€” virtual destructor

CS โ€” virtual destructor

๐Ÿ“• 04/15 - ์†Œ๋ฉธ์ž๋ฅผ Virtual๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š” ์ด์œ 

Notion ์›๋ณธ: https://www.notion.so/343f77b24d2f81b2897ac39040323256 ๋ถ€๋ชจ: ์ž๋ฃŒ โ†’ Cs ๋ฉด์ ‘์ค€๋น„ โ†’ cs ์ฃผ์ œ

๋ชจ์˜๋ฉด์ ‘ ๋‹ต๋ณ€

๊ธฐ๋ฐ˜ ํด๋ž˜์Šค ํฌ์ธํ„ฐ๋กœ ํŒŒ์ƒ ํด๋ž˜์Šค ๊ฐ์ฒด๋ฅผ ๊ฐ€๋ฆฌํ‚จ ๋’ค deleteํ•  ๋•Œ, ์†Œ๋ฉธ์ž๊ฐ€ virtual์ด ์•„๋‹ˆ๋ฉด ๊ธฐ๋ฐ˜ ํด๋ž˜์Šค ์†Œ๋ฉธ์ž๋งŒ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ํŒŒ์ƒ ํด๋ž˜์Šค์—์„œ ํ• ๋‹นํ•œ ์ž์›์ด ํ•ด์ œ๋˜์ง€ ์•Š์•„ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์†Œ๋ฉธ์ž์— virtual์„ ๋ถ™์ด๋ฉด vtable์„ ํ†ตํ•ด ๋™์  ๋””์ŠคํŒจ์น˜๊ฐ€ ์ผ์–ด๋‚˜๊ณ , ํŒŒ์ƒ ํด๋ž˜์Šค ์†Œ๋ฉธ์ž๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด ๊ธฐ๋ฐ˜ ํด๋ž˜์Šค ์†Œ๋ฉธ์ž๊นŒ์ง€ ์˜ฌ๋ฐ”๋ฅธ ์ˆœ์„œ๋กœ ์ „์ฒด ์†Œ๋ฉธ์ž ์ฒด์ธ์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‹คํ˜•์ ์œผ๋กœ ์‚ฌ์šฉ๋  ๊ธฐ๋ฐ˜ ํด๋ž˜์Šค, ์ฆ‰ ๊ฐ€์ƒ ํ•จ์ˆ˜๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์žˆ๋Š” ํด๋ž˜์Šค๋Š” ๋ฐ˜๋“œ์‹œ virtual ์†Œ๋ฉธ์ž๋ฅผ ์„ ์–ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ๊ฐœ๋…

  • virtual ์†Œ๋ฉธ์ž โ€” ์†Œ๋ฉธ์ž๋ฅผ vtable์— ๋“ฑ๋กํ•˜์—ฌ ๋™์  ๋””์ŠคํŒจ์น˜๋กœ ์˜ฌ๋ฐ”๋ฅธ ์†Œ๋ฉธ ์ฒด์ธ ์‹คํ–‰
  • ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ (Memory Leak) โ€” ํŒŒ์ƒ ํด๋ž˜์Šค ์†Œ๋ฉธ์ž ๋ฏธํ˜ธ์ถœ๋กœ ์ž์› ํ•ด์ œ ์‹คํŒจ
  • ์†Œ๋ฉธ์ž ์ฒด์ธ โ€” ํŒŒ์ƒ โ†’ ๊ธฐ๋ฐ˜ ์ˆœ์„œ๋กœ ์†Œ๋ฉธ์ž๊ฐ€ ์ˆœ์ฐจ ํ˜ธ์ถœ (์ƒ์„ฑ์˜ ์—ญ์ˆœ)
  • ์ •์˜๋˜์ง€ ์•Š์€ ๋™์ž‘ (UB) โ€” virtual ์—†๋Š” ๊ธฐ๋ฐ˜ ํฌ์ธํ„ฐ delete ์‹œ C++ ํ‘œ์ค€์˜ ๊ณต์‹ ๊ฒฐ๊ณผ
  • ๋‹คํ˜•์  ๊ธฐ๋ฐ˜ ํด๋ž˜์Šค โ€” ํฌ์ธํ„ฐ/์ฐธ์กฐ๋กœ ํŒŒ์ƒ ๊ฐ์ฒด๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ธฐ๋ฐ˜ ํด๋ž˜์Šค, virtual ์†Œ๋ฉธ์ž ํ•„์ˆ˜

๋ฌธ์ œ ์ƒํ™ฉ โ€” virtual ์—†๋Š” ์†Œ๋ฉธ์ž

๊ธฐ๋ฐ˜ ํด๋ž˜์Šค ํฌ์ธํ„ฐ๋กœ ํŒŒ์ƒ ํด๋ž˜์Šค ๊ฐ์ฒด๋ฅผ deleteํ•˜๋ฉด, ์†Œ๋ฉธ์ž๊ฐ€ non-virtual์ผ ๊ฒฝ์šฐ ์ปดํŒŒ์ผ ํƒ€์ž„์— ์ •์  ๋ฐ”์ธ๋”ฉ์œผ๋กœ ๊ธฐ๋ฐ˜ ํด๋ž˜์Šค ์†Œ๋ฉธ์ž๋งŒ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ํŒŒ์ƒ ํด๋ž˜์Šค ์†Œ๋ฉธ์ž๋Š” ์‹คํ–‰๋˜์ง€ ์•Š์•„ ํŒŒ์ƒ ํด๋ž˜์Šค๊ฐ€ ํ• ๋‹นํ•œ ์ž์›์ด ๊ทธ๋Œ€๋กœ ๋‚จ์Šต๋‹ˆ๋‹ค. C++ ํ‘œ์ค€์€ ์ด๋ฅผ ์ •์˜๋˜์ง€ ์•Š์€ ๋™์ž‘(UB)์œผ๋กœ ๊ทœ์ •ํ•ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Base {
public:
    ~Base() { delete[] data; }   // virtual ์—†์Œ!
protected:
    int* data = new int[10];
};

class Derived : public Base {
public:
    ~Derived() { delete[] extra; }  // ์ ˆ๋Œ€ ํ˜ธ์ถœ ์•ˆ ๋จ!
private:
    float* extra = new float[20];   // 80 bytes ๋ˆ„์ˆ˜
};

Base* ptr = new Derived();
delete ptr;
// ์‹คํ–‰ ๊ฒฐ๊ณผ:
// 1. Base::~Base() ํ˜ธ์ถœ โ†’ data ํ•ด์ œ
// 2. Derived::~Derived() ํ˜ธ์ถœ ์•ˆ ๋จ โ†’ extra 80 bytes ๋ˆ„์ˆ˜!
// C++ ํ‘œ์ค€: Undefined Behavior

์™œ ์ •์  ๋ฐ”์ธ๋”ฉ์ด ์ผ์–ด๋‚˜๋‚˜?

1
2
3
4
5
6
// ์†Œ๋ฉธ์ž๊ฐ€ non-virtual์ด๋ฉด vtable ์Šฌ๋กฏ์— ๋“ฑ๋ก๋˜์ง€ ์•Š์Œ
// delete ptr; ๋Š” ptr์˜ ์ •์  ํƒ€์ž…(Base*)๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์†Œ๋ฉธ์ž ๊ฒฐ์ •
// โ†’ ์ปดํŒŒ์ผ ํƒ€์ž„์— Base::~Base ๋กœ ๊ณ ์ • (๋™์  ๋””์ŠคํŒจ์น˜ ์—†์Œ)

ptr->VirtualFunc();   // vptr โ†’ vtable โ†’ ๋™์  ๋””์ŠคํŒจ์น˜ (Derived ๋ฒ„์ „)
delete ptr;           // virtual ์—†์œผ๋ฉด โ†’ ์ •์  ๋ฐ”์ธ๋”ฉ โ†’ Base::~Base๋งŒ ํ˜ธ์ถœ

๋ณต๊ธฐ:

  • non-virtual ์†Œ๋ฉธ์ž + ๊ธฐ๋ฐ˜ ํฌ์ธํ„ฐ delete = UB + ์ž์› ๋ˆ„์ˆ˜
  • ์Šคํƒ ๊ฐ์ฒด(Derived d;)๋Š” ์ •์  ํƒ€์ž…์œผ๋กœ ์†Œ๋ฉธ๋˜๋ฏ€๋กœ ๋ฌธ์ œ ์—†์Œ
  • ๋ฌธ์ œ๋Š” ์˜ค์ง ํฌ์ธํ„ฐ/์ฐธ์กฐ๋ฅผ ํ†ตํ•œ ๋‹คํ˜•์  ์†Œ๋ฉธ ์ƒํ™ฉ

ํ•ด๊ฒฐ์ฑ… โ€” virtual ์†Œ๋ฉธ์ž

๊ธฐ๋ฐ˜ ํด๋ž˜์Šค ์†Œ๋ฉธ์ž์— virtual์„ ๋ถ™์ด๋ฉด ์†Œ๋ฉธ์ž๊ฐ€ vtable์— ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค. delete ptr ์‹œ vptr โ†’ vtable โ†’ ๋™์  ๋””์ŠคํŒจ์น˜๋กœ ์‹ค์ œ ํƒ€์ž…(Derived)์˜ ์†Œ๋ฉธ์ž๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด ๊ธฐ๋ฐ˜ ํด๋ž˜์Šค ์†Œ๋ฉธ์ž๊นŒ์ง€ ์ž๋™์œผ๋กœ ์ฒด์ธ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Base {
public:
    virtual ~Base() { delete[] data; }  // virtual ์ถ”๊ฐ€!
protected:
    int* data = new int[10];
};

class Derived : public Base {
public:
    ~Derived() override { delete[] extra; }
private:
    float* extra = new float[20];
};

Base* ptr = new Derived();
delete ptr;
// ์‹คํ–‰ ๊ฒฐ๊ณผ:
// 1. vptr โ†’ Derived vtable โ†’ Derived::~Derived() ํ˜ธ์ถœ
// 2. ์ž๋™์œผ๋กœ Base::~Base() ํ˜ธ์ถœ

vtable์—์„œ์˜ ๋™์ž‘

1
2
3
4
5
6
7
8
// Base vtable:    [ ..., Base::~Base    ]
// Derived vtable: [ ..., Derived::~Derived ]

// delete ptr ๋‚ด๋ถ€ ๋™์ž‘:
// 1. ptr->vptr ์ฝ๊ธฐ
// 2. vtable์—์„œ ์†Œ๋ฉธ์ž ์Šฌ๋กฏ ์กฐํšŒ โ†’ Derived::~Derived
// 3. Derived::~Derived() ์‹คํ–‰
// 4. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ž๋™์œผ๋กœ Base::~Base() ํ˜ธ์ถœ

์†Œ๋ฉธ์ž ์ฒด์ธ ์ˆœ์„œ (์ƒ์„ฑ์˜ ์—ญ์ˆœ)

1
2
3
4
5
6
7
8
class A { public: virtual ~A() { /* 3๋ฒˆ์งธ */ } };
class B : public A { public: ~B() override { /* 2๋ฒˆ์งธ */ } };
class C : public B { public: ~C() override { /* 1๋ฒˆ์งธ */ } };

A* ptr = new C();
delete ptr;
// ์†Œ๋ฉธ ์ˆœ์„œ: C::~C โ†’ B::~B โ†’ A::~A
// ์ƒ์„ฑ ์ˆœ์„œ: A::A  โ†’ B::B  โ†’ C::C  (์—ญ์ˆœ)

๋ณต๊ธฐ:

  • ๊ธฐ๋ฐ˜ ์†Œ๋ฉธ์ž์— virtual ํ•˜๋‚˜ ์ถ”๊ฐ€๋กœ ์†Œ๋ฉธ์ž ์ฒด์ธ ์ „์ฒด ๋ณด์žฅ
  • ํŒŒ์ƒ ํด๋ž˜์Šค ์†Œ๋ฉธ์ž๋Š” override ํ‚ค์›Œ๋“œ๋กœ ๋ช…์‹œ ๊ถŒ์žฅ
  • ์†Œ๋ฉธ์ž ์ฒด์ธ์€ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ž๋™ ์ฒ˜๋ฆฌ โ€” ์ˆ˜๋™ ํ˜ธ์ถœ ๋ถˆํ•„์š”

์–ธ์ œ virtual ์†Œ๋ฉธ์ž๊ฐ€ ํ•„์š”ํ•œ๊ฐ€?

๊ฐ€์ƒ ํ•จ์ˆ˜๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์žˆ๋Š” ํด๋ž˜์Šค๋Š” ๋ฐ˜๋“œ์‹œ virtual ์†Œ๋ฉธ์ž๋ฅผ ๊ฐ€์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค. vtable์ด ์ด๋ฏธ ์ƒ์„ฑ๋œ ํด๋ž˜์Šค์—์„œ ์†Œ๋ฉธ์ž๋งŒ non-virtual๋กœ ๋‘๋Š” ๊ฒƒ์€ ์„ค๊ณ„ ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค.

ํŒ๋‹จ ๊ธฐ์ค€

ํด๋ž˜์Šค ํŠน์„ฑvirtual ์†Œ๋ฉธ์ž ํ•„์š”?์ด์œ 
๊ธฐ๋ฐ˜ ํด๋ž˜์Šค๋กœ ์‚ฌ์šฉ๋จโœ… ํ•„์ˆ˜ํŒŒ์ƒ ํฌ์ธํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ delete ๊ฐ€๋Šฅ
final ํด๋ž˜์ŠคโŒ ๋ถˆํ•„์š”์ƒ์† ๋ถˆ๊ฐ€ โ†’ ํŒŒ์ƒ ํด๋ž˜์Šค ์—†์Œ
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() = 0;
    virtual ~Shape() {}        // virtual ์†Œ๋ฉธ์ž ํ•„์ˆ˜!
};

// โœ… final ํด๋ž˜์Šค
class Circle final : public Shape {
public:
    void Draw() override {}
    ~Circle() {}               // virtual ์—†์–ด๋„ ๋จ
};

// โŒ ์ž˜๋ชป๋œ ์„ค๊ณ„
class BadBase {
public:
    virtual void Update() {}
    ~BadBase() {}              // ์œ„ํ—˜!
};

Scott Meyers ๊ทœ์น™ (Effective C++ Item 7)

  • ๊ฐ€์ƒ ํ•จ์ˆ˜๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์žˆ๋‹ค โ†’ virtual ์†Œ๋ฉธ์ž ์„ ์–ธ
  • ๊ฐ€์ƒ ํ•จ์ˆ˜๊ฐ€ ์ „ํ˜€ ์—†๋‹ค โ†’ virtual ์†Œ๋ฉธ์ž ์„ ์–ธ X (๋ถˆํ•„์š”ํ•œ vtable ์˜ค๋ฒ„ํ—ค๋“œ)

์ˆœ์ˆ˜ ๊ฐ€์ƒ ์†Œ๋ฉธ์ž

์†Œ๋ฉธ์ž๋ฅผ ์ˆœ์ˆ˜ ๊ฐ€์ƒ(= 0)์œผ๋กœ ์„ ์–ธํ•ด ์ถ”์ƒ ํด๋ž˜์Šค๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹จ, ์†Œ๋ฉธ์ž๋Š” ์ฒด์ธ ํ˜ธ์ถœ์ด ๋ฐ˜๋“œ์‹œ ์ผ์–ด๋‚˜๋ฏ€๋กœ ์ˆœ์ˆ˜ ๊ฐ€์ƒ์ด๋ผ๋„ ๋ฐ˜๋“œ์‹œ ์ •์˜(๋ณธ๋ฌธ)๋ฅผ ์ œ๊ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class AbstractBase {
public:
    virtual ~AbstractBase() = 0;  // ์ˆœ์ˆ˜ ๊ฐ€์ƒ ์†Œ๋ฉธ์ž
};

// ๋ฐ˜๋“œ์‹œ ์ •์˜ ์ œ๊ณต!
AbstractBase::~AbstractBase() {
    // ๋นˆ ๊ตฌํ˜„์ด์–ด๋„ ๋ฐ˜๋“œ์‹œ ์ž‘์„ฑ
}

class Concrete : public AbstractBase {
public:
    ~Concrete() override {}
};

// AbstractBase obj;  // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ
AbstractBase* p = new Concrete();
delete p;
// Concrete::~Concrete() โ†’ AbstractBase::~AbstractBase() ์ •์ƒ ํ˜ธ์ถœ

์ผ๋ฐ˜ virtual vs ์ˆœ์ˆ˜ virtual ์†Œ๋ฉธ์ž

ํ•ญ๋ชฉvirtual ~Base()virtual ~Base() = 0
์ถ”์ƒ ํด๋ž˜์Šค์•„๋‹˜ (์ธ์Šคํ„ด์Šคํ™” ๊ฐ€๋Šฅ)์ถ”์ƒ (์ธ์Šคํ„ด์Šคํ™” ๋ถˆ๊ฐ€)
์ •์˜ ํ•„์š”์„ ํƒํ•„์ˆ˜ (์ฒด์ธ ํ˜ธ์ถœ)
์‚ฌ์šฉ ๋ชฉ์ ์ผ๋ฐ˜ ๋‹คํ˜• ๊ธฐ๋ฐ˜ ํด๋ž˜์Šค์ˆœ์ˆ˜ ์ธํ„ฐํŽ˜์ด์Šค ๊ฐ•์ œ

๋ณต๊ธฐ:

  • ์ˆœ์ˆ˜ ๊ฐ€์ƒ ์†Œ๋ฉธ์ž = ์ถ”์ƒ ํด๋ž˜์Šค ๋งŒ๋“œ๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•
  • ์ •์˜ ์—†์œผ๋ฉด ๋ง์ปค ์—๋Ÿฌ (undefined reference to ~AbstractBase)
  • ์‹ค๋ฌด์—์„œ๋Š” ์ˆœ์ˆ˜ ๊ฐ€์ƒ ์†Œ๋ฉธ์ž๋ณด๋‹ค ์ธํ„ฐํŽ˜์ด์Šค ํ•จ์ˆ˜๋ฅผ = 0์œผ๋กœ ์„ ์–ธํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋” ์ผ๋ฐ˜์ 
์ด ๊ธฐ์‚ฌ๋Š” ์ €์ž‘๊ถŒ์ž์˜ CC BY 4.0 ๋ผ์ด์„ผ์Šค๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

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

Powered by Jekyll with Chirpy theme

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