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์ผ๋ก ์ ์ธํ๋ ๊ฒฝ์ฐ๊ฐ ๋ ์ผ๋ฐ์