CS โ prevent copy
๐ 04/27 โ ๊ฐ์ฒด ๋ณต์ฌ ๊ธ์ง ๋ชจ์๋ฉด์ ์ค๋น
๋ด์ผ ๋ชจ์๋ฉด์ ์ฃผ์ : โ๊ฐ์ฒด ๋ณต์ฌ๋ฅผ ๋ง๋ ๋ฐฉ๋ฒ์ ์ด๋ค ๋ฐฉ๋ฒ์ด ์์๊น์? ์ ๊ฐ์ฒด ๋ณต์ฌ๋ฅผ ๋ง์์ผ ํ ๊น์?โ ๋จ๋ ์์ ์์ โ
= delete/private/noncopyableโ Rule of Three/Five/Zero โ move-only(unique_ptr) โ ์ฌ๋ผ์ด์ฑ โ virtual ์๋ฉธ์ ๊ผฌ๋ฆฌ์ง๋ฌธ ์ฐ๊ฒฐ ๋ค๋ฆฌ
๋ชจ์๋ฉด์ ๋ต๋ณ
๊ฐ์ฒด ๋ณต์ฌ๋ฅผ ๋ง์์ผ ํ๋ ์ด์ ๋ ํฌ๊ฒ ๋ค ๊ฐ์ง์
๋๋ค. ์ฒซ์งธ, ๋จ๋
์์ ํด์ผ ํ๋ ์์ โ ํ์ผ ํธ๋ค, ๋ฎคํ
์ค, ์์ผ, unique_ptr์ฒ๋ผ ๋ ๊ฐ์ฒด๊ฐ ๊ฐ์ ์์์ ๋ค๊ณ ์์ผ๋ฉด ์ด์ค ํด์ (double free)๋ ์ ๊ธ ์ถฉ๋์ด ๋ฐ์ํฉ๋๋ค. ๋์งธ, ๊ณ ๋น์ฉ ๋ณต์ฌ โ ํฐ ์ปจํ
์ด๋์ ๊น์ ๋ณต์ฌ๋ ์ฑ๋ฅ์ ํฌ๊ฒ ๋จ์ด๋จ๋ฆฝ๋๋ค. ์
์งธ, ์ฑ๊ธํด/๋งค๋์ ๊ฐ์ฒด์ฒ๋ผ ์๋์ ์ผ๋ก ์ธ์คํด์ค๊ฐ 1๊ฐ์ฌ์ผ ํ ๋์
๋๋ค. ๋ท์งธ, ๋คํ ๊ฐ์ฒด์ ์ฌ๋ผ์ด์ฑ(slicing) โ Base b = derived; ์ฒ๋ผ ๋ณต์ฌํ๋ฉด Derived ๋ถ๋ถ์ด ์๋ ค๋๊ฐ๋๋ค. ์์ ๊ฐ์ฒด๋ฅผ ๋ถ๋ชจ ํ์
๋ณ์์ ๊ฐ์ผ๋ก ๋ณต์ฌํ๋ฉด, ์์์์ ์ถ๊ฐยท์ค๋ฒ๋ผ์ด๋ํ ๋ถ๋ถ์ด ๋ ์๊ฐ๋ค
๋ณต์ฌ๋ฅผ ๋ง๋ ๋ฐฉ๋ฒ์ C++11 ๊ธฐ์ค = delete ๊ฐ ํ์ค ๊ถ์ฅ ํจํด์
๋๋ค. ๋ณต์ฌ ์์ฑ์์ ๋ณต์ฌ ๋์
์ฐ์ฐ์์ = delete๋ฅผ ๋ช
์ํด ์ปดํ์ผ ํ์์ ๋ช
ํํ ์๋ฌ ๋ฉ์์ง๋ก ์ฐจ๋จํฉ๋๋ค. C++98 ์ด์ ์๋ private ์ ์ธ + ์ ์ ์ ํจ ํจํด(Boost::noncopyable ์คํ์ผ)์ ์ผ๋๋ฐ, friend ์์์๋ ํธ์ถ ๊ฐ๋ฅํ๊ณ ๋งํฌ ํ์ ์๋ฌ๊ฐ ๋์ ๋ฉ์์ง๊ฐ ๋ชจํธํ๋ค๋ ๋จ์ ์ด ์์ต๋๋ค. ๋ ํ๋์ ํจํด์ move-only ํ์
์ผ๋ก ๋ง๋๋ ๊ฒ โ ๋ณต์ฌ๋ = delete๋ก ๋ง๊ณ move ์์ฑ์/๋์
์ ์ด๋ ค๋๋ฉด unique_ptr์ฒ๋ผ ๋จ๋
์์ + ์์ ๊ถ ์ด์ ์ ํํํ ์ ์์ต๋๋ค.
์ด๋ ๋ฐ๋ผ์ค๋ ๊ท์น์ด Rule of Three / Five / Zero ์
๋๋ค. ์๋ฉธ์ยท๋ณต์ฌ ์์ฑ์ยท๋ณต์ฌ ๋์
์ค ํ๋๋ผ๋ ์ง์ ์ ์ํ๋ฉด ๋๋จธ์ง ๋๋ ์ ์ํด์ผ ํ๋ค๋ ๊ฒ Rule of Three์ด๊ณ , C++11๋ถํฐ move ์์ฑ์/๋์
๊น์ง ์ถ๊ฐํด Rule of Five๊ฐ ๋ฉ๋๋ค. ๊ฐ์ฅ ๊ถ์ฅ๋๋ ๊ฑด Rule of Zero โ RAII ํ์
(unique_ptr, vector ๋ฑ)์ ์์ ๊ด๋ฆฌ๋ฅผ ์์ํ๊ณ ์ฌ์ฉ์๋ ํน๋ณ ๋ฉค๋ฒ ํจ์๋ฅผ ์ ์ํ์ง ์๋ ๊ฒ์
๋๋ค. ์ด์ ์ ๋ฆฌํ ์ค๋งํธ ํฌ์ธํฐ์ unique_ptr์ด ์ ํํ ๋ณต์ฌ = delete + move only ํจํด์ด๋ฉฐ, shared_ptr์ ๋ณต์ฌ ๊ฐ๋ฅํ์ง๋ง ์ฐธ์กฐ ์นด์ดํ
์ผ๋ก ๊ณต์ ์์ ๋ฅผ ์์ ํ๊ฒ ํํํฉ๋๋ค.
ํต์ฌ ๊ฐ๋
- ๊ฐ์ฒด ๋ณต์ฌ (Copy) โ ๋ณต์ฌ ์์ฑ์(
T(const T&))์ ๋ณต์ฌ ๋์ ์ฐ์ฐ์(operator=(const T&))๋ก ๋์ผ ํ์ ๊ฐ์ฒด๋ฅผ ์๋ก ๋ง๋ค๊ฑฐ๋ ๋ฎ์ด์ฐ๋ ๋์ = delete(C++11) โ ํน๋ณ ๋ฉค๋ฒ ํจ์๋ฅผ ๋ช ์์ ์ผ๋ก ์ญ์ . ์ปดํ์ผ ํ์์ ๋ช ํํ ์๋ฌ ๋ฉ์์ง ์ ๊ณต โ ๋ณต์ฌ ์ฐจ๋จ์ ๋ชจ๋ ํ์ค ํจํดprivate์ ์ธ + ์ ์ ์ ํจ (C++98) โ Boost::noncopyable ํจํด. friend ์ฐํ ๊ฐ๋ฅ, ๋งํฌ ์๋ฌ๋ก ๋ฉ์์ง ๋ชจํธ โ ๋ ๊ฑฐ์ ์ฝ๋์์๋ง ๋ฑ์ฅ- noncopyable ๋ฒ ์ด์ค ํด๋์ค โ ๋ณต์ฌ ์ฐจ๋จ์ ์บก์ํํ ๊ธฐ๋ฐ ํด๋์ค ์์(์:
boost::noncopyable,FNoncopyable) - move-only ํ์
โ ๋ณต์ฌ๋
delete, move๋ ํ์ฉ โunique_ptr,thread,lock_guard๊ฐ ๋ํ ์ฌ๋ก - ์์ ๊ถ(Ownership) โ ์์์ ๋๊ฐ ์ฑ ์์ง๊ณ ํด์ ํ ๊ฒ์ธ๊ฐ์ ์๋ฏธ๋ก . ๋จ๋ /๊ณต์ /๊ด์ฐฐ๋ก ๊ตฌ๋ถ
- ์ด์ค ํด์ (Double Free) โ ๊ฐ์ ์์์ ๋ ๋ฒ
deleteํ๋ ๋ฏธ์ ์ ๋์. ๋ณต์ฌ ํ์ฉ + ์๋ฉธ์์์ ํด์ ํ๋ฉด ๋ฐ์ - ๊ฐ์ฒด ์ฌ๋ผ์ด์ฑ (Object Slicing) โ
Base b = derived;์ Derived ๋ถ๋ถ์ด ์๋ ค๋๊ฐ ๋ฐ์ดํฐ ์์ค + vptr์ด Base๋ก ๊ณ ์ ๋๋ ํ์ - Rule of Three โ ์๋ฉธ์ยท๋ณต์ฌ ์์ฑ์ยท๋ณต์ฌ ๋์ ์ฐ์ฐ์๊ฐ ํ ์ธํธ. ํ๋ ์ ์ํ๋ฉด ์ ๋ค ์ ์ (C++98)
- Rule of Five โ Rule of Three + move ์์ฑ์ + move ๋์ ์ฐ์ฐ์ (C++11)
- Rule of Zero โ ์์ ๊ด๋ฆฌ๋ RAII ํ์ ์ ์์ํ๊ณ ์ฌ์ฉ์๋ ํน๋ณ ๋ฉค๋ฒ ํจ์๋ฅผ ์ ์ํ์ง ์๋ ๊ถ์ฅ ๊ด์ฉ๊ตฌ
- ํน๋ณ ๋ฉค๋ฒ ํจ์ (Special Member Functions) โ ์ปดํ์ผ๋ฌ๊ฐ ์๋ ์์ฑํ๋ 6์ข : ๊ธฐ๋ณธ/๋ณต์ฌ/์ด๋ ์์ฑ์, ๋ณต์ฌ/์ด๋ ๋์ ์ฐ์ฐ์, ์๋ฉธ์
- ์๋ฌต์ ์ญ์ (Implicit Deletion) โ ๋ฉค๋ฒ ์ค ํ๋๊ฐ ๋ณต์ฌ ๋ถ๊ฐ๋ฅํ๋ฉด ์ปดํ์ผ๋ฌ๊ฐ ํด๋์ค์ ๋ณต์ฌ ์ฐ์ฐ์ ์๋
delete์ฒ๋ฆฌ - ์ฑ๊ธํด (Singleton) โ ์ธ์คํด์ค๊ฐ ์ ํํ 1๊ฐ์ฌ์ผ ํ๋ ๊ฐ์ฒด. ๋ณต์ฌยท์ด๋ ๋ชจ๋ ์ฐจ๋จํด ์๋ ํํ
UE_NONCOPYABLE/FNoncopyableโ ์ธ๋ฆฌ์ผ์ด ์ ๊ณตํ๋ ๋ณต์ฌ ์ฐจ๋จ ๋งคํฌ๋ก/๋ฒ ์ด์ค. ํ ์ค๋ก Rule of Five์ ๋ณต์ฌ ๋ถ๋ถ ์ฒ๋ฆฌ- ํ์ผ ํธ๋ค (File Handle) โ OS๊ฐ ๊ด๋ฆฌํ๋ ์ปค๋ ๊ฐ์ฒด์ ๋ํ ์ฌ์ฉ์ ๊ณต๊ฐ ์๋ณ์(
FILE*, POSIXfd, WindowsHANDLE). ๋ณต์ฌ ์ ๊ฐ์ ํธ๋ค์ ๊ฐ๋ฆฌ์ผ ์ด์ค close ์ํ - ์ด์ค close (Double Close) โ ๊ฐ์ fd/HANDLE์ ๋ ๋ฒ ๋ซ๋ ๋์. ์ฌ์ด์ ๋ค๋ฅธ ์ค๋ ๋๊ฐ ์ fd๋ฅผ ๊ฐ์ ๋ฒํธ๋ก ๋ฐ์ผ๋ฉด ์๋ฑํ ์์ ๋ซํ โ fd ์ฌ์ฌ์ฉ ๊ณต๊ฒฉ
std::fstream์ด๋ ์ ์ฉ ์ค๊ณ (C++11) โifstream/ofstream/fstream์ด C++11์์ ๋ณต์ฌ= delete, move๋ง ํ์ฉ. RAII๋ก ๋ซํ ๋ณด์ฅ- ๋ฎคํ
์ค (Mutex) โ ์๊ณ ์์ญ ๋ณดํธ์ฉ ๋๊ธฐํ ๊ฐ์ฒด.
std::mutex๋ ๋ณต์ฌยท์ด๋ ๋ชจ๋= delete(์ ๊ธ ์ํ ์๋ฏธ๊ฐ ๋ชจํธ) - ์ ๊ธ ์ํ ์๋ฏธ ๋ชจํธ์ฑ โ ๋ฎคํ ์ค ๋ณต์ฌ ์ โ์ ๊ธด ์ฑ๋ก ๋ณต์ฌ๋๋? ํ๋ฆฐ ์ํ๋ก?โ ๋ต์ด ์์ โ ํ์ค์ ๋ณต์ฌยท์ด๋ ๋ชจ๋ ์ฐจ๋จ
std::lock_guard(move ๋ถ๊ฐ) โ ๋ณต์ฌยท์ด๋ ๋ชจ๋= delete. ์ค์ฝํ RAII์ ๊ฐ์ฅ ๊ฐ๋ฒผ์std::unique_lock(move ๊ฐ๋ฅ) โ ๋ณต์ฌ= delete, move ํ์ฉ. ์ ๊ธ ์์ ๊ถ์ ํจ์ ๊ฐ ์ด์ ๊ฐ๋ฅ โ ์กฐ๊ฑด ๋ณ์์ ํจ๊ป ์ธ ๋ ํ์std::scoped_lock(C++17) โ ๋ณต์ ๋ฎคํ ์ค๋ฅผ ๋ฐ๋๋ฝ ์์ด ์ ๊ทธ๋ RAII. ์ญ์ ๋ณต์ฌยท์ด๋ ์ฐจ๋จ- ์์ผ ๋์คํฌ๋ฆฝํฐ (Socket Descriptor) โ POSIX
int, WinsockSOCKET. fd์ ์ผ์ข ์ด๋ฉฐ ๋์ผํ๊ฒ ์ด์ค closeยท์ฐ๊ฒฐ ์ํ ๊ณต์ ๋ฌธ์ boost::asio::ip::tcp::socket/asio::socketโ ๋ณต์ฌ= delete, move๋ง ํ์ฉ. ๋น๋๊ธฐ ํธ๋ค๋ฌ ์ฒด์ธ์์ ์์ ๊ถ ์ด์ ์ ์ํด move-only ์ค๊ณ- ์ฐ๊ฒฐ ์ํ ๊ณต์ ๋ฌธ์ โ ๊ฐ์ ์์ผ์ ๋ ๊ฐ์ฒด๊ฐ ๋ค๋ฉด ํ์ชฝ์ด send ์ค์ผ ๋ ๋ค๋ฅธ ์ชฝ์ด close โ ๋ถ๋ถ ์ก์ /RST ๋ฐ์, TCP ์ํ ๋จธ์ ์ค์ผ
๋ชฉ์ฐจ
- ํต์ฌ ์์ฝ ์นด๋
- ์ ๋ณต์ฌ๋ฅผ ๋ง์์ผ ํ๋๊ฐ
- ์ด๋ป๊ฒ ๋ณต์ฌ๋ฅผ ๋ง๋๊ฐ
- Rule of Three / Five / Zero
- move-only ํ์
โ
unique_ptrํจํด - ๋จ๋ ์์ ์์ ์ฌํ โ ํ์ผ ํธ๋ค / ๋ฎคํ ์ค / ์์ผ
- ๊ฐ์ฒด ์ฌ๋ผ์ด์ฑ ๋ฐฉ์ง
- ๊ผฌ๋ฆฌ์ง๋ฌธ ์์ ๊ฒฝ๋ก
- ์ธ๋ฆฌ์ผ์์์ ๋ณต์ฌ ๊ธ์ง
1. ํต์ฌ ์์ฝ ์นด๋
์ ๋ง๋๊ฐ 30์ด
1
2
3
4
5
1) ๋จ๋
์์ ์์ โ ํ์ผ/๋ฎคํ
์ค/์์ผ/unique_ptr
๋ณต์ฌํ๋ฉด ์ด์ค ํด์ , ์ ๊ธ ์ถฉ๋
2) ๊ณ ๋น์ฉ ๋ณต์ฌ โ ํฐ ์ปจํ
์ด๋ ๊น์ ๋ณต์ฌ
3) ์ฑ๊ธํด/๋งค๋์ โ ์ธ์คํด์ค 1๊ฐ๊ฐ ๋ณธ์ง
4) ์ฌ๋ผ์ด์ฑ โ Base b = derived ์ Derived ๋ถ๋ถ ์์ค
์ด๋ป๊ฒ ๋ง๋๊ฐ 30์ด
1
2
3
4
5
C++11+ ํ์ค: ๋ณต์ฌ ์์ฑ์/๋์
์ = delete
C++98 ํจํด: private ์ ์ธ + ์ ์ X (Boost::noncopyable)
๋ฒ ์ด์ค ์์: class X : private Noncopyable {}
move-only: ๋ณต์ฌ delete + move ํ์ฉ (unique_ptr ์คํ์ผ)
์ธ์คํด์ค ์ฐจ๋จ: ์์ฑ์/์๋ฉธ์ protected (์ฑ๊ธํด)
Rule of N 30์ด
1
2
3
Rule of Three โ ์๋ฉธ์/๋ณต์ฌ ์์ฑ์/๋ณต์ฌ ๋์
์ค ํ๋ ์ ์ โ ์
๋ค ์ ์
Rule of Five โ + move ์์ฑ์ + move ๋์
(C++11)
Rule of Zero โ RAII ํ์
์ ์์. ํน๋ณ ๋ฉค๋ฒ ํจ์ ์ ์ ์ ํจ (๊ถ์ฅ)
๊ผฌ๋ฆฌ์ง๋ฌธ ์ฐ๊ฒฐ ๋งต
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
๋ณต์ฌ๋ฅผ ๋ง๋ ๋ฐฉ๋ฒ
โโโ = delete (C++11 ํ์ค)
โ โโโ ์ปดํ์ผ ํ์ ๋ช
ํํ ์๋ฌ
โ โโโ private ํจํด ๋๋น friend ์ฐํ ๋ถ๊ฐ
โโโ private + ์ ์ X (C++98)
โ โโโ ๋งํฌ ํ์ ์๋ฌ โ ๋ฉ์์ง ๋ชจํธ
โ โโโ friend ์์์๋ ํธ์ถ ๊ฐ๋ฅ (๊ตฌ๋ฉ)
โโโ noncopyable ๋ฒ ์ด์ค ์์
โ โโโ Boost::noncopyable
โ โโโ UE_NONCOPYABLE / FNoncopyable
โโโ move-only ํ์
(โ
11๋ฒ unique_ptr ์ฐ๊ฒฐ)
โ โโโ ๋ณต์ฌ delete + move ํ์ฉ
โ โโโ std::move๋ก ์์ ๊ถ ์ด์
โโโ ๋จ๋
์์ ์์ ์ฌํ (โ
6์ฅ)
โ โโโ ํ์ผ ํธ๋ค โ fstream move-only, ์ด์ค close, fd ์ฌ์ฌ์ฉ ๊ณต๊ฒฉ
โ โโโ ๋ฎคํ
์ค โ ๋ณต์ฌยท์ด๋ ๋ ๋ค delete, lock_guard/unique_lock/scoped_lock
โ โโโ ์์ผ โ Asio socket move-only, ์ฐ๊ฒฐ ์ํ ๊ณต์ ๋ฌธ์
โโโ Rule of Three / Five / Zero
โ โโโ ์์ ๊ด๋ฆฌ โ RAII ์์ ๊ถ์ฅ
โโโ ์ฌ๋ผ์ด์ฑ ๋ฐฉ์ง (โ
06๋ฒ virtual ์๋ฉธ์ ์ฐ๊ฒฐ)
โโโ ๋คํ ๊ธฐ๋ฐ ํด๋์ค โ noncopyable + virtual ์๋ฉธ์
2. ์ ๋ณต์ฌ๋ฅผ ๋ง์์ผ ํ๋๊ฐ
ํต์ฌ ํ ๋ฌธ์ฅ
โ๊ฐ์ ์์์ ๋ ๊ฐ์ฒด๊ฐ ๋ค๋ฉด ์๋ฏธ๊ฐ ๊นจ์ง๊ฑฐ๋ ์์ ๊ด๋ฆฌ๊ฐ ๋ฌด๋์ง๋ ๊ฒฝ์ฐโ ์ ๋ณต์ฌ๋ฅผ ๋ง์ต๋๋ค. ๋ณต์ฌ ๊ฐ๋ฅํ์ง ์ฌ๋ถ๋ ๋จ์ ํธ์๊ฐ ์๋๋ผ ํ์ ์ ์๋ฏธ๋ก (semantics) ์ ์ผ๋ถ์ ๋๋ค.
์ด์ 1 โ ๋จ๋ ์์ ์์์ ์๋ฏธ ๊นจ์ง
๊ฐ์ฅ ํํ๊ณ ์ค์ํ ์ด์ ์ ๋๋ค. ์์์ ์ฑ ์์๊ฐ ์ ํํ ํ ๋ช ์ด์ด์ผ ์์ ํ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
class FileHandle {
FILE* fp;
public:
FileHandle(const char* path) : fp(fopen(path, "r")) {}
~FileHandle() { if (fp) fclose(fp); } // ์๋ฉธ ์ ๋ซ์
};
FileHandle a("data.txt");
FileHandle b = a; // โ ์ปดํ์ผ๋ฌ๊ฐ ์๋ ์์ฑํ ๋ณต์ฌ โ fp ํฌ์ธํฐ๋ง ๋ณต์ฌ
// a.fp == b.fp (์์ ๋ณต์ฌ)
// ์ค์ฝํ ์ข
๋ฃ ์:
// b ์๋ฉธ โ fclose(fp)
// a ์๋ฉธ โ fclose(fp) โ ๊ฐ์ fp ๋ ๋ฒ ๋ซ์! ๋ฏธ์ ์ ๋์
๊ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ ์์:
- ํ์ผ ํธ๋ค โ ์ด์ค
fcloseโ UB - ๋ฎคํ
์ค โ
lock_guard๋ณต์ฌํ๋ฉด ๊ฐ์ mutex๊ฐ ๋ ๋ฒ unlock โ UB - ์์ผ โ ๊ฐ์ fd๋ฅผ ๋ ๋ฒ close
unique_ptrโ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ ๋ฒdeleteโ ํ ์์- DB ์ปค๋ฅ์ , ํธ๋ค โ ์์ ๊ด๋ฆฌ์๊ฐ ์ถ์ ๋ชป ํจ
์ด์ 2 โ ๊ณ ๋น์ฉ ๋ณต์ฌ
์์์ 1๊ฐ๋ผ๋ ๋ฐ์ดํฐ๊ฐ ๊ฑฐ๋ํ๋ฉด ์๋์น ์์ ๋ณต์ฌ๋ฅผ ๋ง์ ์ฑ๋ฅ์ ๋ณด์ฅํฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
class HugeMatrix {
std::vector<std::vector<double>> data; // ์์ญ MB
public:
// ๋ณต์ฌ๋ฅผ ํ์ฉํ๋ฉด ํจ์ ์ธ์ ํจ์ค์์ ์๋์น ์๊ฒ ๋ฉ๊ฐ๋ฐ์ดํธ ๋ณต์ฌ ๋ฐ์
HugeMatrix(const HugeMatrix&) = delete;
HugeMatrix(HugeMatrix&&) = default; // move๋ ์ด๋ฆผ
};
void Process(HugeMatrix m); // โ ์ด ์์ ์ ์ปดํ์ผ ์๋ฌ๋ก ์ฌ์ฉ์์๊ฒ ๊ฒฝ๊ณ
// Process(std::move(m)) ์ผ๋ก๋ง ํธ์ถ ๊ฐ๋ฅ
์ด์ 3 โ ์ฑ๊ธํด / ๋งค๋์ ๊ฐ์ฒด
์ธ์คํด์ค๊ฐ 1๊ฐ์์ ํ์ ์์ฒด๋ก ๊ฐ์ ํฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class GameManager {
public:
static GameManager& Instance() {
static GameManager s;
return s;
}
private:
GameManager() = default;
~GameManager() = default;
GameManager(const GameManager&) = delete;
GameManager& operator=(const GameManager&) = delete;
// move๋ ๋ณดํต ํจ๊ป ๋ง์
GameManager(GameManager&&) = delete;
GameManager& operator=(GameManager&&) = delete;
};
์ด์ 4 โ ๊ฐ์ฒด ์ฌ๋ผ์ด์ฑ(Slicing)
๋คํ ๊ฐ์ฒด๋ฅผ ๊ฐ์ผ๋ก ๋ณต์ฌํ๋ฉด ํ์ ํด๋์ค(Derived = Base๋ฅผ ์์ํ ์์ ํด๋์ค, ์๋ ์์์ Circle)์์ ์๋ก ์ถ๊ฐํ ๋ฉค๋ฒยท์ค๋ฒ๋ผ์ด๋ ์ ๋ณด๊ฐ ํต์งธ๋ก ์๋ ค๋๊ฐ๋๋ค.
์ฉ์ด ์ ๋ฆฌ
- Base(๊ธฐ๋ฐ ํด๋์ค): ์์์ ๋ถ๋ชจ. ์์์
Shape.- Derived(ํ์ ํด๋์ค):
Base๋ฅผ ์์ํด ๋ฉค๋ฒ๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ๊ฐ์ ํจ์๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ ์์. ์์์Circle.- ์๋ ค๋๊ฐ๋ ๋ถ๋ถ: Derived๊ฐ Base ์์ ๋ง๋ถ์ธ ์ถ๊ฐ ๋ฉค๋ฒ(
Circle::r)์ ๊ฐ์ ํจ์ ๋์คํจ์น ์ ๋ณด(vptr โCircle::Area).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Shape {
public:
virtual double Area() const { return 0; }
};
class Circle : public Shape {
double r;
public:
Circle(double r) : r(r) {}
double Area() const override { return 3.14 * r * r; }
};
Circle c(5);
Shape s = c; // โ ์ฌ๋ผ์ด์ฑ! Circle::r ์๋ ค๋๊ฐ, vptr๋ Shape๋ก ๊ณ ์
s.Area(); // 0 ๋ฐํ (Circle::Area ํธ์ถ ์ ๋จ)
๋คํ ํด๋์ค๋ ๋ณดํต ์ถ์ ํด๋์ค๋ก ๋ง๋ค๊ฑฐ๋(์์ ๊ฐ์), ๋ณต์ฌ ์์ฒด๋ฅผ ๋ง์ ์ฌ๋ผ์ด์ฑ์ ์ฐจ๋จํฉ๋๋ค โ 06_virtual_destructor.md ์ฐ๊ฒฐ ๋ค๋ฆฌ.
์๋ฏธ๋ก ์ ๋ฆฌํ
| ์์ ์ข ๋ฅ | ๋ณต์ฌ ์๋ฏธ | ๊ถ์ฅ ์ฒ๋ฆฌ |
|---|---|---|
| ํ์ผ/์์ผ/๋ฎคํ ์ค | ์ด์ค ํด์ ยท์ ๊ธ ์ถฉ๋ | = delete (move-only) |
unique_ptr | ๋จ๋ ์์ ๊นจ์ง | = delete (move-only) |
shared_ptr | ๊ณต์ ์์ โ ์นด์ดํธ ์ฆ๊ฐ | ๋ณต์ฌ ํ์ฉ |
| ํฐ ๋ฐ์ดํฐ(POD) | ๊น์ ๋ณต์ฌ ๋น์ฉ | ๋ณดํต ํ์ฉ + ์๋์ ๋ง๊ธฐ |
| ์ฑ๊ธํด/๋งค๋์ | ์ธ์คํด์ค ์ค๋ณต | = delete ๋ณต์ฌ+์ด๋ |
| ๋คํ ๊ธฐ๋ฐ ํด๋์ค | ์ฌ๋ผ์ด์ฑ ์ํ | = delete ๋๋ ์ถ์ํ |
3. ์ด๋ป๊ฒ ๋ณต์ฌ๋ฅผ ๋ง๋๊ฐ
ํต์ฌ ํ ๋ฌธ์ฅ
C++11๋ถํฐ
= delete๊ฐ ํ์ค ๊ถ์ฅ ํจํด์ ๋๋ค. C++98 ์์ ์privateํธ๋ฆญ์ ๋ ์ด์ ์ธ ์ด์ ๊ฐ ์์ต๋๋ค.
๋ฐฉ๋ฒ 1 โ = delete (C++11, ๋ชจ๋ ๊ถ์ฅ)
1
2
3
4
5
6
7
8
9
10
11
class NoCopy {
public:
NoCopy() = default;
NoCopy(const NoCopy&) = delete; // ๋ณต์ฌ ์์ฑ์ ์ญ์
NoCopy& operator=(const NoCopy&) = delete; // ๋ณต์ฌ ๋์
์ญ์
};
NoCopy a;
NoCopy b = a; // โ error: call to deleted constructor of 'NoCopy'
NoCopy c;
c = a; // โ error: overload resolution selected deleted operator '='
์ฅ์ :
- ์ปดํ์ผ ํ์ ๋ช
ํํ ์๋ฌ โ
deleted function๋ฉ์์ง๊ฐ ์ฆ์ ๋์ด - friend ์์์๋ ํธ์ถ ๋ถ๊ฐ โ
privateํธ๋ฆญ์ ๊ตฌ๋ฉ์ ๋ง์ - ์๋๊ฐ ์ฝ๋์ ๋ช
์์ โ public ์์ญ์์
delete๋ก ์ ์ธํ๋ ๊ฒ ๊ฐ๋ ์ฑ ์ข์
๋ฐฉ๋ฒ 2 โ private ์ ์ธ + ์ ์ ์ ํจ (C++98 ํจํด)
1
2
3
4
5
6
7
class NoCopy {
public:
NoCopy() {}
private:
NoCopy(const NoCopy&); // ์ ์ธ๋ง, ์ ์ ์์
NoCopy& operator=(const NoCopy&); // ์ ์ธ๋ง, ์ ์ ์์
};
๋จ์ :
- friend ํจ์/๋ฉค๋ฒ ํจ์ ์์์๋ ํธ์ถ ๊ฐ๋ฅ โ ๋งํฌ ํ์์์ผ ์๋ฌ
- ์๋ฌ ๋ฉ์์ง๊ฐ ๋ชจํธ โ
undefined reference to NoCopy::NoCopy(NoCopy const&) - C++11 ์ดํ ์ฌ์ฉ ์ด์ ์์ โ
= delete๋ก ๊ต์ฒดํด์ผ ํจ
๋ฐฉ๋ฒ 3 โ noncopyable ๋ฒ ์ด์ค ํด๋์ค ์์
Boost::noncopyable ์คํ์ผ๋ก ๋ณต์ฌ ์ฐจ๋จ์ ์บก์ํํ ๋ฒ ์ด์ค๋ฅผ ์์ํฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
class Noncopyable {
protected:
Noncopyable() = default;
~Noncopyable() = default;
public:
Noncopyable(const Noncopyable&) = delete;
Noncopyable& operator=(const Noncopyable&) = delete;
};
class Database : private Noncopyable {
// ์๋์ผ๋ก ๋ณต์ฌ ๋ถ๊ฐ
};
์ฅ์ : ํด๋์ค๋ง๋ค ๋ ์ค์ฉ ์ฐ์ง ์์๋ ๋จ, ์๋ ๋ช ํ ๋จ์ : ๋จ์ผ ์์ ์ฌ๋กฏ ์ฐจ์ง(๋ณดํต private ์์์ด๋ผ ํฐ ๋ฌธ์ ๋ ์๋), EBO๋ก ํฌ๊ธฐ ์ฆ๊ฐ๋ ์์
๋ฐฉ๋ฒ 4 โ move-only ํ์
๋ณต์ฌ๋ = delete, move๋ ํ์ฉํฉ๋๋ค โ 11๋ฒ unique_ptr ํจํด๊ณผ ๋์ผ.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class FileHandle {
FILE* fp = nullptr;
public:
explicit FileHandle(const char* path) : fp(fopen(path, "r")) {}
~FileHandle() { if (fp) fclose(fp); }
// ๋ณต์ฌ ๊ธ์ง
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
// move ํ์ฉ โ ์์ ๊ถ ์ด์
FileHandle(FileHandle&& o) noexcept : fp(o.fp) { o.fp = nullptr; }
FileHandle& operator=(FileHandle&& o) noexcept {
if (this != &o) {
if (fp) fclose(fp);
fp = o.fp;
o.fp = nullptr;
}
return *this;
}
};
FileHandle a("data.txt");
FileHandle b = std::move(a); // โ
์์ ๊ถ ์ด์ . a.fp == nullptr
// ์ด์ค fclose ์์ โ a ์๋ฉธ์๊ฐ nullptr ์ฒดํฌ
์ด๊ฒ ์ ํํ std::unique_ptr์ด ๊ตฌํ๋ ๋ฐฉ์์
๋๋ค โ 11๋ฒ [์ค๋งํธ ํฌ์ธํฐ] ์ฐ๊ฒฐ ๋ค๋ฆฌ.
๋ฐฉ๋ฒ 5 โ ์์ฑ์/์๋ฉธ์ protected (์ธ์คํด์คํ ์์ฒด ์ ์ด)
์ฑ๊ธํด์ด๋ ํฉํ ๋ฆฌ ์ ์ฉ ๊ฐ์ฒด๋ ์ธ๋ถ์์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค ์ ์๊ฒ ํฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
class Logger {
public:
static Logger& Instance() {
static Logger l;
return l;
}
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
protected:
Logger() = default; // ์ธ๋ถ์์ ์ง์ ์์ฑ ๋ถ๊ฐ
~Logger() = default;
};
๋น๊ต ํ
| ๋ฐฉ๋ฒ | ์๊ธฐ | ์๋ฌ ์์ | ๋ฉ์์ง | friend ์ฐํ | ๊ถ์ฅ |
|---|---|---|---|---|---|
= delete | C++11+ | ์ปดํ์ผ | ๋ช ํ | ๋ถ๊ฐ | โ ํ์ค |
private + no def | C++98 | ๋งํฌ | ๋ชจํธ | ๊ฐ๋ฅ | ๋ ๊ฑฐ์๋ง |
| noncopyable ์์ | ์์ชฝ | ์ปดํ์ผ/๋งํฌ | ๋ช ํ | ๋ถ๊ฐ | ๋ณด์ผ๋ฌํ๋ ์ดํธ ์ค์ด๊ธฐ |
| move-only | C++11+ | ์ปดํ์ผ | ๋ช ํ | ๋ถ๊ฐ | ์์ ๊ด๋ฆฌ |
protected ์์ฑ์ | ์์ชฝ | ์ปดํ์ผ | ๋ช ํ | ๋ถ๊ฐ(ํ์ ์ธ) | ์ฑ๊ธํด |
์๋ฌต์ ์ญ์ โ ์ปดํ์ผ๋ฌ๊ฐ ์๋์ผ๋ก ๋ง๋ ๊ฒฝ์ฐ
์๊ธฐ๋ = delete๋ฅผ ์ ์ธํ์ง ์์๋, ๋ฉค๋ฒ ์ค ํ๋๊ฐ ๋ณต์ฌ ๋ถ๊ฐ๋ฅํ๋ฉด ์ปดํ์ผ๋ฌ๊ฐ ์๋์ผ๋ก ํด๋์ค์ ๋ณต์ฌ๋ฅผ delete ์ฒ๋ฆฌํฉ๋๋ค.
1
2
3
4
5
6
7
class Container {
std::unique_ptr<int> data; // unique_ptr์ด ๋ณต์ฌ ๋ถ๊ฐ
};
Container a;
Container b = a; // โ ์๋์ผ๋ก deleted โ Container์ ๋ณต์ฌ ์์ฑ์๊ฐ ์๋ฌต ์ญ์ ๋จ
Container c = std::move(a); // โ
move๋ ์๋ ์์ฑ๋จ
์ฆ, ๋ฉค๋ฒ์ unique_ptr์ ๋๋ ๊ฒ๋ง์ผ๋ก๋ ํด๋์ค๊ฐ ์๋์ผ๋ก move-only๊ฐ ๋ฉ๋๋ค. Rule of Zero์ ์ด์์ ํ์ฉ.
4. Rule of Three / Five / Zero
ํต์ฌ ํ ๋ฌธ์ฅ
์๋ฉธ์๊ฐ ์์์ ํด์ ํ๋ค๋ฉด, ๋ณต์ฌยท์ด๋ ์๋ฏธ๋ก ๋ ํจ๊ป ์ ์ํด์ผ ์ผ๊ด๋ฉ๋๋ค. ๊ฐ์ฅ ์ข์ ๊ฑด ์ ์๋ฅผ ์ ํ๊ณ RAII ํ์ ์ ์์ํ๋ ๊ฒ(Rule of Zero)์ ๋๋ค.
Rule of Three (C++98)
์๋ฉธ์, ๋ณต์ฌ ์์ฑ์, ๋ณต์ฌ ๋์ ์ฐ์ฐ์ โ ํ๋๋ผ๋ ์ง์ ์ ์ํ๋ฉด ์ ๋ค ์ ์ํด์ผ ํฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class BadString {
char* data;
public:
BadString(const char* s) {
data = new char[strlen(s) + 1];
strcpy(data, s);
}
~BadString() { delete[] data; } // โ ์ง์ ์ ์
// ๋ณต์ฌ ์์ฑ์/๋์
์ ์ปดํ์ผ๋ฌ๊ฐ ๊ธฐ๋ณธ ์์ฑ (์์ ๋ณต์ฌ!)
};
BadString a("hello");
BadString b = a; // ์์ ๋ณต์ฌ โ a.data == b.data
// ์ค์ฝํ ์ข
๋ฃ โ ๋ ๋ค ๊ฐ์ data delete[] โ ์ด์ค ํด์ UB!
ํด๊ฒฐ โ Rule of Three ์ค์:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class GoodString {
char* data;
public:
GoodString(const char* s) {
data = new char[strlen(s) + 1];
strcpy(data, s);
}
// 1) ์๋ฉธ์
~GoodString() { delete[] data; }
// 2) ๋ณต์ฌ ์์ฑ์ (๊น์ ๋ณต์ฌ)
GoodString(const GoodString& o) {
data = new char[strlen(o.data) + 1];
strcpy(data, o.data);
}
// 3) ๋ณต์ฌ ๋์
(์๊ธฐ ๋์
+ ๊น์ ๋ณต์ฌ)
GoodString& operator=(const GoodString& o) {
if (this != &o) {
delete[] data;
data = new char[strlen(o.data) + 1];
strcpy(data, o.data);
}
return *this;
}
};
Rule of Five (C++11+)
C++11๋ถํฐ ์ด๋ ์์ฑ์์ ์ด๋ ๋์ ์ฐ์ฐ์๊ฐ ์ถ๊ฐ๋ฉ๋๋ค. ์๋ฉธ์/๋ณต์ฌ ์ ์ค ํ๋๋ผ๋ ์ง์ ์ ์ํ๋ฉด 5์ข ๋ชจ๋ ์ ์(๋๋ ๋ช ์์ ์ญ์ ) ํด์ผ ํฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class GoodString {
char* data;
public:
GoodString(const char* s) { /* ... */ }
// 1) ์๋ฉธ์
~GoodString() { delete[] data; }
// 2) ๋ณต์ฌ ์์ฑ์
GoodString(const GoodString& o) { /* deep copy */ }
// 3) ๋ณต์ฌ ๋์
GoodString& operator=(const GoodString& o) { /* ... */ }
// 4) ์ด๋ ์์ฑ์ (์์ ๊ถ ์ด์ )
GoodString(GoodString&& o) noexcept : data(o.data) {
o.data = nullptr;
}
// 5) ์ด๋ ๋์
GoodString& operator=(GoodString&& o) noexcept {
if (this != &o) {
delete[] data;
data = o.data;
o.data = nullptr;
}
return *this;
}
};
๋ณต์ฌ๋ฅผ ๋ง๊ณ ์ถ๋ค๋ฉด 2ยท3์ = delete๋ก โ Rule of Five์ ํ ํํ:
1
2
3
4
5
6
7
8
class FileHandle {
public:
~FileHandle();
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
FileHandle(FileHandle&&) noexcept;
FileHandle& operator=(FileHandle&&) noexcept;
};
Rule of Zero (๊ถ์ฅ)
์์ ๊ด๋ฆฌ๋ RAII ํ์ ์ ์์ํ๊ณ ์ฌ์ฉ์๋ ํน๋ณ ๋ฉค๋ฒ ํจ์๋ฅผ ํ๋๋ ์ ์ํ์ง ์์ต๋๋ค.
1
2
3
4
5
6
7
8
9
10
class GoodString {
std::string data; // string์ด ์์์ ์์ ๊ด๋ฆฌ
public:
GoodString(const char* s) : data(s) {}
// ์๋ฉธ์/๋ณต์ฌ/์ด๋ ๋ชจ๋ ์ปดํ์ผ๋ฌ ๊ธฐ๋ณธ ์์ฑ์ผ๋ก ์ถฉ๋ถ
};
GoodString a("hello");
GoodString b = a; // โ
๊น์ ๋ณต์ฌ (string์ ๋์)
GoodString c = std::move(a); // โ
์ด๋
1
2
3
4
5
6
7
8
9
10
class Player {
std::unique_ptr<Inventory> inv; // unique_ptr์ด ์์์ ๋จ๋
์์
std::vector<Item> items; // vector๊ฐ ์์์ ๊น์ ๋ณต์ฌ/์ด๋
std::string name;
public:
// ์๋ฌด๊ฒ๋ ์ ์ ์ ํจ โ Player๋ ์๋์ผ๋ก:
// - ๋ณต์ฌ ๊ฐ๋ฅ (๋ชจ๋ ๋ฉค๋ฒ๊ฐ ๋ณต์ฌ ๊ฐ๋ฅํ๋ฉด)
// - unique_ptr ๋๋ฌธ์ ์ฌ์ค์ move-only๋ก ์๋ ๋ณํ
// - ์๋ฉธ์๋ ์์์ ๋์
};
๋น๊ต ํ
| ๊ท์น | ์๊ธฐ | ์ ์ํด์ผ ํ ๋ฉค๋ฒ | ๊ถ์ฅ๋ |
|---|---|---|---|
| Rule of Three | C++98 | ์๋ฉธ์ + ๋ณต์ฌ ์์ฑ์ + ๋ณต์ฌ ๋์ | ์์ ์ง์ ๊ด๋ฆฌ ์ |
| Rule of Five | C++11+ | + ์ด๋ ์์ฑ์ + ์ด๋ ๋์ | ์์ ์ง์ ๊ด๋ฆฌ ์ |
| Rule of Zero | C++11+ | ์์ (RAII ํ์ ์ฌ์ฉ) | โ ์ต์ฐ์ ๊ถ์ฅ |
5. move-only ํ์
โ unique_ptr ํจํด
ํต์ฌ ํ ๋ฌธ์ฅ
๋ณต์ฌ๋
= delete, ์ด๋์ ํ์ฉ โ ์์์ ๋จ๋ ์์ ๋ฅผ ํํํ๋ฉด์๋ ํจ์ ์ธ์/๋ฐํ์ ์์ ๋กญ๊ฒ ํ๋ ํจํด์ ๋๋ค.std::unique_ptr์ด ์ ํํ ์ด ํํ์ ๋๋ค โ 11๋ฒ [์ค๋งํธ ํฌ์ธํฐ] ์ฐ๊ฒฐ.
unique_ptr์ ์ค์ ์ ์ (๊ฐ๋ตํ)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
template <typename T>
class unique_ptr {
T* ptr;
public:
explicit unique_ptr(T* p = nullptr) : ptr(p) {}
~unique_ptr() { delete ptr; }
// ๋ณต์ฌ ๋ช
์์ ์ญ์
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
// ์ด๋ ํ์ฉ โ ์์ ๊ถ ์ด์
unique_ptr(unique_ptr&& o) noexcept : ptr(o.ptr) { o.ptr = nullptr; }
unique_ptr& operator=(unique_ptr&& o) noexcept {
if (this != &o) {
delete ptr;
ptr = o.ptr;
o.ptr = nullptr;
}
return *this;
}
T* get() const { return ptr; }
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
};
move-only๊ฐ ํจ์ ์ธ์/๋ฐํ์์ ์๋ํ๋ ๋ฐฉ์
1
2
3
4
5
6
7
8
9
10
11
12
std::unique_ptr<Widget> Make() {
return std::make_unique<Widget>(); // ๋ฐํ ์ move
}
void Take(std::unique_ptr<Widget> w) { // ์์ ๊ถ ์ด์ ๋ฐ์
w->DoWork();
} // w ์๋ฉธ โ delete
auto p = Make(); // โ
๋ฐํ๊ฐ move
// Take(p); // โ ๋ณต์ฌ ๋ถ๊ฐ
Take(std::move(p)); // โ
๋ช
์์ move โ ์์ ๊ถ ์ด์
// ์ด ์์ ์ p == nullptr
๊ทธ๋ผ ์ move๋ ์ด๋ฆฌ๋๊ฐ?
๋ณต์ฌ๋ฅผ ๋ง๋๋ค๊ณ ๊ฐ์ฒด๊ฐ ๋ค๋ฅธ ํจ์๋ก ์ด๋์กฐ์ฐจ ๋ชป ํ๋ฉด ์ฌ์ฉ์ฑ์ด ๋๋ฌด ๋์ฉ๋๋ค. ์ด๋์ โ์๋ณธ์ ๋น์ฐ๊ณ ์ ๊ฐ์ฒด๋ก ์์์ ์ฎ๊ธฐ๋โ ์๋ฏธ ์ด๋ฏ๋ก ๋จ๋ ์์ ๋ฅผ ๊นจ์ง ์์ต๋๋ค.
1
2
๋ณต์ฌ โ A์ ์์์ ๊ทธ๋๋ก ๋๊ณ B๊ฐ ๊ฐ์ ์์์ ๋ ๊ฐ์ง (์ด์ค ์์ โ)
์ด๋ โ A์ ์์์ B๋ก ์ฎ๊ธฐ๊ณ A๋ ๋น ์ํ (๋จ๋
์์ ์ ์ง โ
)
move-only๊ฐ ์ ํฉํ ์์
- ๋์ ๋ฉ๋ชจ๋ฆฌ (
unique_ptr) - ์ค๋ ๋ ํธ๋ค (
std::thread) - ์ ๊ธ ๊ฐ์ฒด (
std::lock_guard๋ move๋ ๋ง์,std::unique_lock์ ํ์ฉ) - ํ์ผ ์คํธ๋ฆผ (
std::ifstreamโ C++11๋ถํฐ move-only) - ์์ผ/๋ฎคํ ์ค ๋ํผ
๊ฐ ์์์ ๋ณต์ฌ/์ด๋ ์ ์ฑ ์ด ์ ๊ทธ๋ ๊ฒ ๊ฒฐ์ ๋์๋์ง๋ ๋ค์ 6์ฅ์์ ์์ธํ ๋ค๋ฃน๋๋ค.
6. ๋จ๋ ์์ ์์ ์ฌํ โ ํ์ผ ํธ๋ค / ๋ฎคํ ์ค / ์์ผ
ํต์ฌ ํ ๋ฌธ์ฅ
ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ณต์ฌ๋ฅผ = deleteํ๊ณ move๋ง ์ด๋ฆฐ ์ด์ ๋ ์์์ ์๋ฏธ๋ก ๋๋ฌธ์ ๋๋ค. OS ํธ๋คยท์ ๊ธยท์์ผ์ ๋ชจ๋ โ๋์์ ๋ ์ฑ ์์๊ฐ ๋ค ์ ์๋ ์์โ์ด๋ผ move-only๊ฐ ์ ๋ต์ ๋๋ค.
6-1. ํ์ผ ํธ๋ค (File Handle)
OS๋ ์ด๋ฆฐ ํ์ผ์ ์ปค๋์์ ๊ด๋ฆฌํ๊ณ , ์ฌ์ฉ์ ๊ณต๊ฐ์๋ ๊ทธ ์์์ ๊ฐ๋ฆฌํค๋ ์๋ณ์๋ง ๋ ธ์ถํฉ๋๋ค.
| ํ๋ซํผ | ์๋ณ์ ํ์ | ๋ซ๊ธฐ ํจ์ |
|---|---|---|
| C ํ์ค | FILE* | fclose |
| POSIX | int (file descriptor) | close |
| Windows | HANDLE (void*) | CloseHandle |
๋ณต์ฌ ์ ๋ฌด์์ด ์ํํ๊ฐ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ์ง์ ๋ง๋ ์ ์ข์ ์
class MyFile {
int fd = -1;
public:
MyFile(const char* path) : fd(::open(path, O_RDONLY)) {}
~MyFile() { if (fd >= 0) ::close(fd); }
// ์ปดํ์ผ๋ฌ ๊ธฐ๋ณธ ๋ณต์ฌ = ์์ ๋ณต์ฌ โ fd ์ ์๋ง ๋ณต์ฌ๋จ
};
MyFile a("data.txt"); // fd = 5
MyFile b = a; // a.fd == b.fd == 5
// ์ค์ฝํ ์ข
๋ฃ:
// b ์๋ฉธ โ close(5) OK
// a ์๋ฉธ โ close(5) โ ์ด๋ฏธ ๋ซํ fd ์ฌ close
// ์ฌ์ด์ ๋ค๋ฅธ ์ค๋ ๋๊ฐ socket() ๋ฑ์ผ๋ก fd 5๋ฅผ ๋ฐ์ผ๋ฉด
// ์๋ฑํ ์์์ด ๋ซํ fd ์ฌ์ฌ์ฉ ๊ณต๊ฒฉ(use-after-close) ๋ฐ์
์ด ๋ฌธ์ ๋๋ฌธ์ POSIX๋ close ํ ๊ฐ์ fd ๋ฒํธ๊ฐ ์ฆ์ ์ฌ์ฌ์ฉ๋ ์ ์๋ค๋ ์ ์ด ๋ณด์ ์ด์๋ก ์์ฃผ ๊ฑฐ๋ก ๋ฉ๋๋ค.
ํ์ค ํด๊ฒฐ โ std::fstream์ C++11์์ move-only๋ก ์ค๊ณ
1
2
3
std::ifstream a("data.txt");
// std::ifstream b = a; // โ deleted copy ctor
std::ifstream b = std::move(a); // โ
ํธ๋ค ์ด์ . a๋ ๋ซํ ์ํ
std::ifstream ์ ์(๊ฐ๋
์ ):
1
2
3
4
5
6
7
8
class basic_ifstream {
public:
basic_ifstream(const basic_ifstream&) = delete;
basic_ifstream& operator=(const basic_ifstream&) = delete;
basic_ifstream(basic_ifstream&&);
basic_ifstream& operator=(basic_ifstream&&);
~basic_ifstream(); // ๋ด๋ถ filebuf๊ฐ close
};
RAII๋ก ์ค์ฝํ ์ข ๋ฃ ์ ์๋ close โ โ๋ซ๋ ๊ฑธ ์์๋คโ ๋ฒ๊ทธ๊ฐ ์ปดํ์ผ๋ฌ ์ฐจ์์์ ์ฌ๋ผ์ง.
6-2. ๋ฎคํ ์ค (Mutex)
๋ฎคํ ์ค๋ ์ ๊ธ ์ํ ์์ฒด๊ฐ ์์์ด๋ผ ๋ณต์ฌ ์๋ฏธ๊ฐ ๋ณธ์ง์ ์ผ๋ก ๋ชจํธํฉ๋๋ค.
1
2
3
4
5
std::mutex m1;
// std::mutex m2 = m1; // โ deleted โ ๊ทธ๋ด๋ฏํ ๋ต์ด ์กด์ฌํ์ง ์์
// - ์ ๊ธด ์ํ๋ก ๋ณต์ฌ? ๋ mutex๊ฐ ๊ฐ์ ์ ๊ธ์ ๊ณต์ ?
// - ํ๋ฆฐ ์ํ๋ก ๋ณต์ฌ? ๊ทธ๋ฌ๋ฉด ์๋ณธ ์ ๊ธ์ ์ด๋ป๊ฒ ๋จ?
// - ์ด๋ ๋ต์ด๋ race condition์ ๋ง๋ ๋ค
ํ์ค์ ๋ณต์ฌยท์ด๋ ๋ชจ๋ ์ฐจ๋จํ๋ ๊ฒ์ด ๊ฐ์ฅ ์์ ํ ๊ฒฐ๋ก ์ด๋ผ๊ณ ํ๋จํ์ต๋๋ค.
1
2
3
4
5
6
7
8
class mutex {
public:
mutex() = default;
~mutex();
mutex(const mutex&) = delete;
mutex& operator=(const mutex&) = delete;
// ์ด๋๋ ์ ์๋์ง ์์ โ std::mutex๋ ๋น-์ด๋ ํ์
};
๊ทธ๋ผ ์ ๊ธ์ ํจ์ ๊ฐ์ ์ด๋ป๊ฒ ์ฎ๊ธฐ๋? โ RAII ๋ํผ 3์ข ์ ์ฌ์ฉํฉ๋๋ค.
| ๋ํผ | ๋ณต์ฌ | ์ด๋ | ์ฉ๋ |
|---|---|---|---|
std::lock_guard | delete | delete | ๊ฐ์ฅ ๊ฐ๋ฒผ์ด ์ค์ฝํ ์ ๊ธ. ํจ์ ์์์๋ง |
std::unique_lock | delete | default | ์ ๊ธ ์์ ๊ถ ์ด์ ๊ฐ๋ฅ. condition_variable๊ณผ ํจ๊ป ํ์ |
std::scoped_lock (C++17) | delete | delete | ๋ณต์ ๋ฎคํ
์ค ๋ฐ๋๋ฝ ๋ฐฉ์ง(std::lock ์๊ณ ๋ฆฌ์ฆ ๋ด์ฅ) |
unique_lock์ด move-only์ธ ์ด์ :
1
2
3
4
5
6
7
8
9
10
11
std::unique_lock<std::mutex> AcquireLock(std::mutex& m) {
std::unique_lock<std::mutex> lk(m);
// ... ๊ฒ์ฆ
return lk; // โ
move๋ก ํธ์ถ์์๊ฒ ์ ๊ธ ์์ ๊ถ ์ด์
}
void Worker(std::condition_variable& cv, std::mutex& m) {
auto lk = AcquireLock(m);
cv.wait(lk, [&]{ return ready; }); // โ unique_lock ํ์
// wait ๋ด๋ถ๊ฐ lk.unlock()/lock()์ ํธ์ถ โ lock_guard๋ก๋ ๋ถ๊ฐ๋ฅ
}
condition_variable::wait์ ์ ๊ธ์ ์ผ์์ ์ผ๋ก ํ์๋ค๊ฐ ๋ค์ ์ก์์ผ ํ๋ฏ๋ก ์ ๊ธ ์์ ๊ถ ์ด์ ์ด ๊ฐ๋ฅํ unique_lock์ด ํ์ํฉ๋๋ค. lock_guard๋ก๋ ๋์ ์์ฒด๊ฐ ๋ถ๊ฐ๋ฅ.
6-3. ์์ผ (Socket)
์์ผ fd๋ ํ์ผ fd์ ๊ฐ์ ๋ฉ์ปค๋์ฆ์ด์ง๋ง ์ฐ๊ฒฐ ์ํ์ ์ก์์ ๋ฒํผ๊ฐ ์ถ๊ฐ๋ก ๋ฐ๋ผ์ต๋๋ค.
1
2
3
4
5
// POSIX
int s = ::socket(AF_INET, SOCK_STREAM, 0);
// int s2 = s; // ์ ์ ๋ณต์ฌ โ ๊ฐ์ ์ปค๋ ๊ฐ์ฒด ๋ ์ฑ
์์
// close(s); // ํ์ชฝ์ด ๋ซ์ผ๋ฉด TCP ์ฐ๊ฒฐ RST ์ก์
// send(s2, ...) // ๋ค๋ฅธ ์ชฝ์ ์ด๋ฏธ ๋๊ธด ์์ผ์ ์ก์ โ EBADF or EPIPE
์ฐ๊ฒฐ ์ํ ๊ณต์ ๋ฌธ์ :
- ํ์ชฝ์ด
send๋์ค ๋ค๋ฅธ ์ชฝ์ดcloseโ ๋ถ๋ถ ์ก์ , RST ํจํท ๋ฐ์ - ํ์ชฝ์ด
recv๋๊ธฐ ์ค ๋ค๋ฅธ ์ชฝ์ด close โ ๋๊ธฐ ํจ์๊ฐ 0 ๋๋ ์๋ฌ๋ก ๊นจ์ง - TCP ์ํ ๋จธ์ (ESTABLISHED โ FIN_WAIT โ โฆ)์ด ๋ ๊ฐ์ฒด ์ฌ์ด์์ ์ผ๊ด์ฑ ์์ด ์งํ
Boost.Asio / ๋ ๋ฆฝ Asio์ ์์ผ ์ค๊ณ:
1
2
3
4
5
6
7
8
9
namespace asio = boost::asio;
asio::io_context ctx;
asio::ip::tcp::socket sock(ctx);
sock.connect(endpoint);
// asio::ip::tcp::socket s2 = sock; // โ deleted copy
asio::ip::tcp::socket s2 = std::move(sock); // โ
move โ ์์ ๊ถ ์ด์
// ์ด ์์ ์ sock์ ๋น ์ํ(์ฐ๊ฒฐ ์์)
Asio ์์ผ ํด๋์ค ์ ์(๊ฐ๋ ์ ):
1
2
3
4
5
6
7
8
9
template <typename Protocol>
class basic_socket {
public:
basic_socket(const basic_socket&) = delete;
basic_socket& operator=(const basic_socket&) = delete;
basic_socket(basic_socket&&) noexcept;
basic_socket& operator=(basic_socket&&) noexcept;
~basic_socket(); // close() ํธ์ถ
};
์ ๋น๋๊ธฐ ์ฝ๋์์ move-only๊ฐ ํนํ ์ค์ํ๊ฐ?
๋น๋๊ธฐ ํธ๋ค๋ฌ๋ ์ฝ๋ฐฑ ์ฒด์ธ ํํ๋ก ์คํ๋๋ฉฐ, ์์ผ์ ์๋ช ์ด ์ฌ๋ฌ ์ฝ๋ฐฑ์ ๊ฑฐ์ณ ์ด์ด์ง๋๋ค. ๋ณต์ฌ๊ฐ ๊ฐ๋ฅํ๋ฉด ํธ๋ค๋ฌ๋ง๋ค ์์ผ์ ๋ณต์ฌํด ๋ค๊ณ ๋ค๋ ์ ์๋๋ฐ, ์ด๋ ๊ณง ๊ฐ์ fd๊ฐ ์ฌ๋ฌ ํธ๋ค๋ฌ์ ์ฐ์ฌํด close ํ์ด๋ฐ์ด ๋ฌด๋์ง๋๋ค. move-only์ด๊ธฐ ๋๋ฌธ์ ์์ผ ์์ ๊ถ์ด ํ ์์ ์ ์ ํํ ํ ํธ๋ค๋ฌ์๋ง ์๋ค๋ ๋ถ๋ณ์ฑ์ด ๊ฐ์ ๋ฉ๋๋ค.
1
2
3
4
5
6
asio::async_read(sock, buf, [s = std::move(sock)](auto ec, auto n) mutable {
// ๋๋ค๊ฐ ์์ผ ์์ โ ๋ค์ ํธ๋ค๋ฌ๋ก ๋ค์ std::move
asio::async_write(s, buf, [s = std::move(s)](auto ec, auto n) {
// ์ฝ๋ฐฑ ์ฒด์ธ ๋๊น์ง ๋จ๋
์์ ๋ณด์ฅ
});
});
์์๋ณ ๋น๊ต ์ข ํฉ
| ์์ | ํ์ค ํ์ | ๋ณต์ฌ | ์ด๋ | ์ํ (๋ณต์ฌ ์) |
|---|---|---|---|---|
| ํ์ผ | std::ifstream/ofstream | delete | โ | ์ด์ค close, fd ์ฌ์ฌ์ฉ ๊ณต๊ฒฉ |
| ๋ฎคํ ์ค | std::mutex | delete | delete | ์ ๊ธ ์ํ ์๋ฏธ ๋ชจํธ |
| ์ค์ฝํ ์ ๊ธ | std::lock_guard | delete | delete | ์ด์ค unlock UB |
| ์ ๊ธ ์์ ๊ถ | std::unique_lock | delete | โ | (์ด๋ ๊ฐ๋ฅ โ cv.wait์ฉ) |
| ๋ณต์ ์ ๊ธ | std::scoped_lock | delete | delete | ๋ฐ๋๋ฝ ๋ฐฉ์ง ์๊ณ ๋ฆฌ์ฆ |
| ์์ผ (Asio) | asio::ip::tcp::socket | delete | โ | ์ฐ๊ฒฐ ์ํ ๊ณต์ , RST, ๋ถ๋ถ ์ก์ |
| ๋์ ๋ฉ๋ชจ๋ฆฌ | std::unique_ptr | delete | โ | ์ด์ค delete, ํ ์์ |
๊ณตํต ํจํด: ๋ชจ๋ ์์ ํธ๋ค + ์๋ฉธ์ release + ๋ณต์ฌ delete + (ํ์ ์) move ํ์ฉ = RAII + move-only. ์ฌ์ฉ์ ์ฝ๋๋ ์ด ํ์ค ํ์ ๋ค์ ๋ฉค๋ฒ๋ก ๋๊ธฐ๋ง ํ๋ฉด ์๋ฌต์ ์ญ์ ๋ก ์๋ move-only๊ฐ ๋ฉ๋๋ค โ Rule of Zero.
7. ๊ฐ์ฒด ์ฌ๋ผ์ด์ฑ ๋ฐฉ์ง
ํต์ฌ ํ ๋ฌธ์ฅ
๋คํ ๊ฐ์ฒด๋ฅผ ๊ฐ์ผ๋ก ๋ณต์ฌํ๋ฉด Derived ๋ถ๋ถ์ด ์๋ ค๋๊ฐ vptr๊น์ง Base๋ก ๊ณ ์ ๋ฉ๋๋ค. ๋คํ ๊ธฐ๋ฐ ํด๋์ค๋ ๋ณดํต noncopyable + virtual ์๋ฉธ์ ์กฐํฉ์ผ๋ก ์ค๊ณํฉ๋๋ค โ
06_virtual_destructor.md์ฐ๊ฒฐ.
์ฌ๋ผ์ด์ฑ ๋์
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Animal {
public:
virtual ~Animal() = default;
virtual void Speak() const { std::cout << "..." << std::endl; }
};
class Dog : public Animal {
std::string breed = "Shiba";
public:
void Speak() const override { std::cout << "Woof! (" << breed << ")\n"; }
};
Dog d;
Animal a = d; // ์ฌ๋ผ์ด์ฑ! Dog::breed ์๋ ค๋๊ฐ, a์ vptr์ Animal vtable ๊ฐ๋ฆฌํด
a.Speak(); // "..." ์ถ๋ ฅ (Dog::Speak ํธ์ถ ์ ๋จ)
// ํจ์ ์ธ์์์๋ ์์ฃผ ๋ฐ์
void Process(Animal a) { a.Speak(); } // โ ๊ฐ์ผ๋ก ๋ฐ์ โ ์ฌ๋ผ์ด์ฑ
void Process(const Animal& a) { a.Speak(); } // โ
์ฐธ์กฐ โ ๋คํ์ฑ ์ ์ง
ํด๊ฒฐ 1 โ ๋คํ ํด๋์ค๋ฅผ noncopyable๋ก
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Animal {
public:
Animal() = default;
virtual ~Animal() = default;
// ๋ณต์ฌ ๊ธ์ง โ ์ฌ๋ผ์ด์ฑ ์์ฒ ์ฐจ๋จ
Animal(const Animal&) = delete;
Animal& operator=(const Animal&) = delete;
virtual void Speak() const = 0; // ์ถ์ ํด๋์ค๋ก๋ ๋ง๋ฆ
};
Animal a = d; // โ ์ปดํ์ผ ์๋ฌ
Animal& ar = d; // โ
์ฐธ์กฐ๋ OK
์ด ํจํด์ ๋คํ ๊ฐ์ฒด๋ ํญ์ ํฌ์ธํฐ/์ฐธ์กฐ๋ก๋ง ๋ค๋ฃฌ๋ค๋ ๊ด์ฉ๊ตฌ๋ฅผ ๊ฐ์ ํฉ๋๋ค.
ํด๊ฒฐ 2 โ ์ถ์ ๊ธฐ๋ฐ ํด๋์ค(์์ ๊ฐ์ ํจ์)
1
2
3
4
5
6
7
8
9
class Animal {
public:
virtual ~Animal() = default;
virtual void Speak() const = 0; // ์์ ๊ฐ์ โ ์ถ์ ํด๋์ค
};
Animal a; // โ ์ถ์ ํด๋์ค๋ ์ธ์คํด์คํ ๋ถ๊ฐ
Animal a2 = d; // โ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ถ๊ฐ
Animal* p = new Dog(); // โ
ํฌ์ธํฐ๋ก๋ง ๊ฐ๋ฅ
๋คํ ๊ธฐ๋ฐ ํด๋์ค ๊ถ์ฅ ๊ตฌ์ฑ
1
2
3
4
5
6
7
8
9
10
11
12
13
class Shape {
public:
Shape() = default;
virtual ~Shape() = default; // 1) virtual ์๋ฉธ์
Shape(const Shape&) = delete; // 2) ๋ณต์ฌ ๊ธ์ง
Shape& operator=(const Shape&) = delete;
Shape(Shape&&) = default; // 3) move๋ ๋ณดํต ์ฐจ๋จ
Shape& operator=(Shape&&) = default; // ๋๋ delete
virtual double Area() const = 0; // 4) ์ถ์ํ
};
์ด๊ฒ 6๋ฒ [virtual ์๋ฉธ์] + 12๋ฒ [๋ณต์ฌ ๊ธ์ง] + 11๋ฒ [์ค๋งํธ ํฌ์ธํฐ๋ก ๋ค๋ฃจ๊ธฐ] ์ ํตํฉ ํจํด์ ๋๋ค.
1
2
std::unique_ptr<Shape> s = std::make_unique<Circle>(5.0);
s->Area(); // ๋คํ์ฑ ์ ์ง, ์ฌ๋ผ์ด์ฑ ์์, virtual ์๋ฉธ์๋ก ์์ ํด์
8. ๊ผฌ๋ฆฌ์ง๋ฌธ ์์ ๊ฒฝ๋ก
๋ฉ์ธ ์ง๋ฌธ ๋ต๋ณ ํ ์์ ํ๋ฆ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
"๊ฐ์ฒด ๋ณต์ฌ๋ฅผ ๋ง๋ ๋ฐฉ๋ฒ, ์ ๋ง์์ผ ํ๋?"
โ
โโ ์ ๋ง๋๊ฐ (4๊ฐ์ง ์ด์ )
โ โโ "๋จ๋
์์ ์์์ด๋ ๊ตฌ์ฒด์ ์ผ๋ก?"
โ โโ "์ด์ค ํด์ ๊ฐ ์ ์ํํ๊ฐ์?"
โ โโ "๊ทธ๋ผ unique_ptr๋ ์ด๋ป๊ฒ ๋์?" โ
11๋ฒ ํ๊ท
โ
โโ ์ด๋ป๊ฒ ๋ง๋๊ฐ
โ โโ "= delete vs private ์ฐจ์ด?"
โ โ โโ "C++11 ์ด์ ์ ์ด๋ป๊ฒ ํ๋์?"
โ โโ "noncopyable ๋ฒ ์ด์ค์ ์ฅ๋จ์ ?"
โ
โโ Rule of N
โ โโ "Rule of Zero๊ฐ ๊ฐ์ฅ ์ข์ ์ด์ ?"
โ โโ "์๋ฌต์ ์ญ์ ๋ ์ด๋ป๊ฒ ๋์?"
โ
โโ move-only
โ โโ "์ ๋ณต์ฌ๋ ๋ง๊ณ move๋ ์ด๋ฆฌ๋์?"
โ โโ "unique_ptr ๋ด๋ถ ๊ตฌ์กฐ๋?" โ
11๋ฒ ํ๊ท
โ
โโ ์ฌ๋ผ์ด์ฑ
โโ "์ฌ๋ผ์ด์ฑ์ด ์ ํํ ๋ฌด์?"
โโ "๋คํ ํด๋์ค๋ ์ด๋ป๊ฒ ์ค๊ณ?"
โโ "virtual ์๋ฉธ์๋ ๊ฐ์ด ํ์ํ๊ฐ?" โ
6๋ฒ ํ๊ท
โโ "๊ทธ๋ผ vtable์ด ์ด๋ค ์ญํ ?" โ
5,8๋ฒ ํ๊ท
๊ฐ ๊ผฌ๋ฆฌ์ง๋ฌธ 30์ด ๋ต๋ณ
Q: ๋ณต์ฌ๋ฅผ ์ ๋ง์์ผ ํ๋์?
1
2
3
4
5
6
7
๋ค ๊ฐ์ง ์ด์ :
1) ๋จ๋
์์ ์์ โ ํ์ผ/๋ฎคํ
์ค/์์ผ/unique_ptr.
๋ณต์ฌํ๋ฉด ๊ฐ์ ์์์ ๋ ์ฑ
์์๊ฐ ์๊ฒจ ์ด์ค ํด์ ยท์ ๊ธ ์ถฉ๋.
2) ๊ณ ๋น์ฉ ๋ณต์ฌ โ ํฐ ์ปจํ
์ด๋ ๊น์ ๋ณต์ฌ ๋น์ฉ ์ฐจ๋จ.
3) ์ฑ๊ธํด/๋งค๋์ โ ์ธ์คํด์ค๊ฐ 1๊ฐ์ฌ์ผ ํ๋ ๊ฐ์ฒด.
4) ์ฌ๋ผ์ด์ฑ โ Base b = derived ์ Derived ๋ถ๋ถ ์์ค.
์ฆ "๋ณต์ฌ๊ฐ ์๋ฏธ์ ๊นจ์ง๋ ํ์
"์ ๋ง์์ผ ํฉ๋๋ค.
Q: = delete์ private ํธ๋ฆญ์ ์ฐจ์ด?
1
2
3
4
5
6
7
8
9
= delete (C++11):
- ์ปดํ์ผ ํ์ ๋ช
ํํ ์๋ฌ ๋ฉ์์ง
- friend ์์์๋ ํธ์ถ ๋ถ๊ฐ
- public ์์ญ์ ๋ช
์์ ์ผ๋ก ์๋ ํํ
private + ์ ์ X (C++98):
- ๋งํฌ ํ์ ์๋ฌ โ ๋ฉ์์ง ๋ชจํธ
- friend ํจ์/๋ฉค๋ฒ ํจ์ ์์์๋ ํธ์ถ ๊ฐ๋ฅ (๊ตฌ๋ฉ)
- C++11 ์ดํ๋ก๋ ์ฌ์ฉ ์ด์ ์์
Q: Rule of Three / Five / Zero๊ฐ ๋ญ๊ฐ์?
1
2
3
4
5
Rule of Three โ ์๋ฉธ์/๋ณต์ฌ ์์ฑ์/๋ณต์ฌ ๋์
์ค ํ๋ ์ ์ โ ์
๋ค ์ ์
์์ ์ง์ ๊ด๋ฆฌ(new/delete) ์ ์ผ๊ด์ฑ ์ ์ง์ฉ
Rule of Five โ + move ์์ฑ์ + move ๋์
(C++11)
Rule of Zero โ RAII ํ์
(string/vector/unique_ptr)์ ์์,
ํน๋ณ ๋ฉค๋ฒ ํจ์ ์ ์ ์ ํจ. ๊ฐ์ฅ ๊ถ์ฅ.
Q: ์ ๋ณต์ฌ๋ ๋ง๊ณ move๋ ์ด๋ฆฌ๋์?
1
2
3
4
5
6
๋ณต์ฌ: ์๋ณธ ๊ทธ๋๋ก ๋๊ณ ์ ๊ฐ์ฒด๊ฐ ๊ฐ์ ์์์ ๋ ๊ฐ์ง โ ๋จ๋
์์ ๊นจ์ง
์ด๋: ์๋ณธ์ ๋น์ฐ๊ณ ์ ๊ฐ์ฒด๋ก ์์์ ์ฎ๊น โ ๋จ๋
์์ ์ ์ง
unique_ptr์ด ์ด ํจํด์ ์ ์:
๋ณต์ฌ = delete, move ํ์ฉ โ ํจ์ ์ธ์/๋ฐํ์ std::move๋ก ๊ฐ๋ฅ
๋จ๋
์์ ๋ผ๋ ์๋ฏธ๋ฅผ ๊นจ์ง ์์ผ๋ฉด์ ์ ์ฐ์ฑ ํ๋ณด
Q: ์ฌ๋ผ์ด์ฑ์ด ๋ญ๊ฐ์?
1
2
3
4
5
6
7
class Base { virtual void f(); };
class Derived : public Base { int extra; };
Derived d;
Base b = d; // ์ฌ๋ผ์ด์ฑ! d.extra ์๋ ค๋๊ฐ, b.vptr์ Base vtable
b.f(); // Base::f ํธ์ถ (๋คํ์ฑ ๊นจ์ง)
// ๋คํ ๊ธฐ๋ฐ ํด๋์ค๋ ๋ณดํต ๋ณต์ฌ๋ฅผ ๋ง๊ณ ํฌ์ธํฐ/์ฐธ์กฐ๋ก๋ง ๋ค๋ฃจ๋๋ก ์ค๊ณ
Q: ๋ฉค๋ฒ์ unique_ptr์ด ์์ผ๋ฉด ์๋์ผ๋ก ์ด๋ป๊ฒ ๋๋์?
1
2
3
4
์ปดํ์ผ๋ฌ๊ฐ ํด๋์ค์ ๋ณต์ฌ ์์ฑ์/๋์
์ ์๋ฌต์ ์ผ๋ก = delete ์ฒ๋ฆฌ.
์ด์ : unique_ptr์ด ๋ณต์ฌ ๋ถ๊ฐ โ ๋ฉค๋ฒ ๋ณต์ฌ๊ฐ ๋ถ๊ฐ๋ฅ
๊ฒฐ๊ณผ: ์ฌ์ฉ์๊ฐ ์๋ฌด๊ฒ๋ ์ ์จ๋ ํด๋์ค๊ฐ ์๋์ผ๋ก move-only.
์ด๊ฒ์ด Rule of Zero์ ์ด์์ ํ์ฉ โ ์์ ๊ด๋ฆฌ ์์์ ํจ๊ณผ.
Q: ๋คํ ๊ธฐ๋ฐ ํด๋์ค๋ ์ด๋ป๊ฒ ์ค๊ณํด์ผ ํ๋์?
1
2
3
4
5
6
7
ํ์ค ํจํด 4์ข
:
1) virtual ~Base() โ ๊ธฐ๋ฐ ํฌ์ธํฐ delete ์ ํ์ ์๋ฉธ์ ์ฒด์ธ ํธ์ถ ๋ณด์ฅ
2) ๋ณต์ฌ = delete โ ์ฌ๋ผ์ด์ฑ ์ฐจ๋จ, ํญ์ ํฌ์ธํฐ/์ฐธ์กฐ๋ก๋ง ๋ค๋ฃจ๊ธฐ
3) move๋ ๋ณดํต = delete โ ์์ ํ ๊ธฐ๋ณธ๊ฐ
4) ์์ ๊ฐ์ ํจ์ โ ์ถ์ ํด๋์ค๋ก ๋ง๋ค๋ฉด ์ธ์คํด์คํ ์์ฒด ๋ถ๊ฐ
์ฌ์ฉ ์: std::unique_ptr<Base>๋ก ๋คํ ๊ฐ์ฒด๋ฅผ ๋ค๋ฃธ โ 11๋ฒ ์ค๋งํธ ํฌ์ธํฐ ์ฐ๊ฒฐ
Q: std::fstream์ด ๋ณต์ฌ ๋ถ๊ฐ์ธ ์ด์ ๋?
1
2
3
4
5
6
ํ์ผ ํธ๋ค(FILE*/fd)์ OS๊ฐ ๊ด๋ฆฌํ๋ ๋จ๋
์์.
๋ณต์ฌํ๋ฉด ๊ฐ์ fd๋ฅผ ๋ ๊ฐ์ฒด๊ฐ ๋ค์ด ์ด์ค close ๋ฐ์.
์ด์ค close๋ ๋จ์ ์๋ฌ๊ฐ ์๋๋ผ fd ์ฌ์ฌ์ฉ ๊ณต๊ฒฉ์ผ๋ก ์ด์ด์ง ์ ์์ โ
๋ค๋ฅธ ์ค๋ ๋๊ฐ ๊ฐ์ fd ๋ฒํธ๋ก ์ ์์์ ๋ฐ์ ์์ ์ ๋ ๋ฒ์งธ close๊ฐ
์ผ์ด๋๋ฉด ์๋ฑํ ์์์ด ๋ซํ ์ ๋ณด ๋์ถ/์์ ๊ฐ๋ฅ.
C++11์์ ifstream/ofstream/fstream์ move-only๋ก ์ค๊ณํด RAII ๋ซ๊ธฐ ๋ณด์ฅ.
Q: std::mutex๋ ์ ์ด๋์กฐ์ฐจ ์ ๋๋์?
1
2
3
4
5
6
7
8
๋ฎคํ
์ค๋ ์ ๊ธ ์ํ ์์ฒด๊ฐ ์์์ด๋ผ ๋ณต์ฌยท์ด๋ ์๋ฏธ๊ฐ ๋ณธ์ง์ ์ผ๋ก ๋ชจํธ:
- ์ ๊ธด ์ฑ๋ก ์ฎ๊ธฐ๋ฉด? ์ฎ๊ธด ํ unlock ์ฑ
์์ ๋๊ตฌ?
- ํ์ด์ ์ฎ๊ธฐ๋ฉด? ์๊ณ ์์ญ ๋ณดํธ๊ฐ ๊นจ์ง
์ด๋ค ๋ต์ ๊ณจ๋ผ๋ race condition์ ๋ง๋ค ์ ์์ด ํ์ค์ ๋ ๋ค = delete.
๋์ RAII ๋ํผ๋ก ์ ๊ธ์ ๋ค๋ฃธ:
lock_guard โ ๊ฐ์ฅ ๊ฐ๋ฒผ์. ๋ณต์ฌยท์ด๋ ๋ชจ๋ delete
unique_lock โ ์ด๋ ํ์ฉ. condition_variable.wait()์ด ์๊ตฌ
scoped_lock โ ๋ณต์ ๋ฎคํ
์ค ๋ฐ๋๋ฝ ๋ฐฉ์ง(C++17)
Q: unique_lock๊ณผ lock_guard์ ์ฐจ์ด๋?
1
2
3
4
5
6
7
8
9
10
lock_guard:
- ๋ณต์ฌยท์ด๋ ๋ชจ๋ = delete
- ๊ฐ์ฅ ๊ฐ๋ฒผ์ (fast path ์ ๊ธ)
- ํจ์ ์ ์ค์ฝํ ์ ๊ธ ์ ์ฉ
unique_lock:
- ๋ณต์ฌ = delete, move ํ์ฉ
- ์ ๊ธ์ ํจ์ ๊ฐ ์ด์ ๊ฐ๋ฅ (๋ฐํยท์ธ์ ํจ์ค)
- cv.wait()์ฒ๋ผ ์ผ์์ ์ผ๋ก unlock/lock ํด์ผ ํ๋ API์ ํ์
- ์ฝ๊ฐ ๋ ๋ฌด๊ฑฐ์ (์์ ์ฌ๋ถ ํ๋๊ทธ ๋ณด์ )
์ ํ ๊ธฐ์ค: cv.wait ๋๋ ์ ๊ธ ์ด์ ํ์ โ unique_lock, ๊ทธ ์ธ โ lock_guard
Q: ์์ผ์ ๋ณต์ฌํ๋ฉด ์ ์ํํ๊ฐ์?
1
2
3
4
5
6
7
8
์์ผ์ fd + ์ฐ๊ฒฐ ์ํ(TCP state) + ์ก์์ ๋ฒํผ๊ฐ ๋ฌถ์ธ ์์.
๋ณต์ฌ ์ ๊ฐ์ fd๋ฅผ ๋ ๊ฐ์ฒด๊ฐ ๋ค๋ฉด:
1) ํ์ชฝ send ์ค ๋ค๋ฅธ ์ชฝ close โ ๋ถ๋ถ ์ก์ /RST ํจํท, TCP ์ํ ์ค์ผ
2) ํ์ชฝ recv ๋๊ธฐ ์ค close โ ๋๊ธฐ ํจ์๊ฐ 0/์๋ฌ๋ก ๊นจ์ง
3) ์ด์ค close๋ก fd ์ฌ์ฌ์ฉ ๊ณต๊ฒฉ
Boost.Asio/asio๋ socket์ move-only๋ก ์ค๊ณ โ ๋น๋๊ธฐ ํธ๋ค๋ฌ ์ฒด์ธ์์
์์ผ ์์ ๊ถ์ด ํ ์์ ์ ์ ํํ ํ ํธ๋ค๋ฌ์๋ง ์๋๋ก ๊ฐ์ .
๋๋ค ์บก์ฒ์์ std::move(sock)์ผ๋ก ์ด์ ํ๋ฉฐ ์ฝ๋ฐฑ ์ฒด์ธ ์งํ.
Q: shared_ptr์ ์ ๋ณต์ฌ๊ฐ ๊ฐ๋ฅํ๊ฐ์?
1
2
3
4
5
6
shared_ptr์ ์๋ฏธ๋ "๊ณต์ ์์ " โ ์ฌ๋ฌ ๊ฐ์ฒด๊ฐ ํจ๊ป ์์์ ์ฑ
์์ง.
๋ณต์ฌ ์ ์ฐธ์กฐ ์นด์ดํธ๋ง ์ฆ๊ฐ์ํค๋ฏ๋ก ์์ ์์ฒด๋ ์ฌ์ ํ 1๊ฐ.
strong_count == 0์ด ๋์ด์ผ delete ๋จ โ ์ด์ค ํด์ ์ ์ผ์ด๋จ.
์ฆ ๋ณต์ฌ๊ฐ ์์ ํ ์ด์ ๋ shared_ptr์ด "๋จ๋
์์ "๊ฐ ์๋ "๊ณต์ ์์ "
์๋ฏธ๋ก ์ ๊ฐ๊ธฐ ๋๋ฌธ. ๋ณต์ฌ ๊ฐ๋ฅ/๋ถ๊ฐ๋ฅ์ ํ์
์ ์๋ฏธ๊ฐ ๊ฒฐ์ ํจ.
(11๋ฒ ์ค๋งํธ ํฌ์ธํฐ ํ๊ณ 1ยท4๋ฒ๊ณผ ๋์ผ ํ๋ฆ)
9. ์ธ๋ฆฌ์ผ์์์ ๋ณต์ฌ ๊ธ์ง
๋ ๊ฐ์ง ๋ณต์ฌ ์ ์ฑ โ UObject vs ์ผ๋ฐ C++ ๊ฐ์ฒด
1
2
3
4
5
6
7
8
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ UObject ๊ณ์ด โ ์ผ๋ฐ C++ ๊ฐ์ฒด โ
โ (AActor, UComponent ๋ฑ) โ (FVector, ์ปค์คํ
struct) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ ๋ณธ์ง์ ์ผ๋ก ๋ณต์ฌ ๋ถ๊ฐ โ USTRUCT / ์ผ๋ฐ C++ ๊ท์น โ
โ NewObject<T>๋ก๋ง ์์ฑ โ ๋ณต์ฌ ํ์ฉ ๋๋ โ
โ GC๊ฐ ๋จ๋
์ฑ
์ โ UE_NONCOPYABLE / FNoncopyableโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
UE_NONCOPYABLE ๋งคํฌ๋ก
์ธ๋ฆฌ์ผ์ด ์ ๊ณตํ๋ ํ ์ค noncopyable ์ ์ธ์ ๋๋ค.
1
2
3
4
5
6
7
8
class FMyResource
{
public:
FMyResource();
~FMyResource();
UE_NONCOPYABLE(FMyResource) // โ ํ ์ค๋ก ๋ณต์ฌยท์ด๋ ๋ชจ๋ = delete
};
UE_NONCOPYABLE์ ๋งคํฌ๋ก๋ก ๋ค์๊ณผ ๊ฐ์ด ํ์ฅ๋ฉ๋๋ค(๊ฐ๋
์ ):
1
2
3
#define UE_NONCOPYABLE(TypeName) \
TypeName(const TypeName&) = delete; \
TypeName& operator=(const TypeName&) = delete;
์ธ๋ฆฌ์ผ ๊ณต์ ๊ฐ์ด๋๋ผ์ธ์ ์์ ๊ด๋ฆฌ ํด๋์ค์ ์ด ๋งคํฌ๋ก๋ฅผ ๊ถ์ฅํฉ๋๋ค.
FNoncopyable ๋ฒ ์ด์ค ํด๋์ค
1
2
3
4
class FMyManager : private FNoncopyable
{
// ์๋์ผ๋ก ๋ณต์ฌ ๋ถ๊ฐ
};
๋ด๋ถ์ ์ผ๋ก ์ ํจํด(noncopyable ๋ฒ ์ด์ค ์์)์ ๊ทธ๋๋ก ์ ์ฉํ ํํ์ ๋๋ค.
UObject๋ ์ ๋ณธ์ง์ ์ผ๋ก ๋ณต์ฌ ๋ถ๊ฐ์ธ๊ฐ?
1
2
3
4
5
6
7
8
9
10
11
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
};
AMyActor a; // โ ์ปดํ์ผ ์๋ฌ โ UObject๋ ์คํ์ ๋ชป ๋ง๋ฆ
AMyActor* p = new AMyActor();// โ ์๋ชป๋ ์ฌ์ฉ โ operator new๊ฐ GC ํ๋ก ๋ผ์ฐํ
๋จ
// โ
์ฌ๋ฐ๋ฅธ ์์ฑ
AMyActor* Actor = NewObject<AMyActor>(this);
์ด์ :
UObject๋ GC๊ฐ ๋จ๋ ๊ด๋ฆฌ โ ๋ ์ธ์คํด์ค๊ฐ ๊ฐ์ ์์์ ๋ค๋ฉด GC ์ถ์ ๊นจ์ง- ์์ฑ์๊ฐ
protected/GENERATED_BODY()๊ฐ ์๋ ์์ฑํ๋ ๊ธฐ๋ฐ ๊ตฌ์กฐ๊ฐ ๋ณต์ฌ ์ฐจ๋จ AActor๋World๋ ๋ฒจ์ ๋ฑ๋ก๋ ๋จ๋ ๊ฐ์ฒด โ ์๋ฏธ์ ๋ณต์ฌ ๋ถ๊ฐ
TUniquePtr / TSharedPtr ์ ๋ณต์ฌ ์ ์ฑ
11๋ฒ์์ ๋ณธ ๊ฒ๊ณผ ๋์ผํ๊ฒ:
| ํ์ | ๋ณต์ฌ | ์ด๋ | ๋น๊ณ |
|---|---|---|---|
TUniquePtr<T> | โ delete | โ | move-only โ unique_ptr ๋์ |
TSharedPtr<T> | โ (์นด์ดํธ++) | โ | ๊ณต์ ์์ โ shared_ptr ๋์ |
TSharedRef<T> | โ (null ๋ถ๊ฐ) | โ | ํญ์ ์ ํจํ ๊ณต์ ์์ |
TWeakPtr<T> | โ (๊ด์ฐฐ๋ง) | โ | ์ฝํ ์ฐธ์กฐ |
TWeakObjectPtr<T> | โ (UObject ์ฝํ ์ฐธ์กฐ) | โ | GC ํ๊ฒฝ ์ฝํ ์ฐธ์กฐ |
1
2
3
TUniquePtr<FMyData> a = MakeUnique<FMyData>();
// TUniquePtr<FMyData> b = a; // โ ๋ณต์ฌ ๋ถ๊ฐ
TUniquePtr<FMyData> b = MoveTemp(a); // โ
move (std::move ๋์)
USTRUCT(BlueprintType) ์ ๋ณต์ฌ ์๋ฏธ
USTRUCT๋ ๋ณดํต ๊ฐ ํ์
โ ๋ณต์ฌ ๊ฐ๋ฅ์ ๊ธฐ๋ณธ์ผ๋ก ํฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
USTRUCT(BlueprintType)
struct FInventoryItem
{
GENERATED_BODY()
UPROPERTY() FName Name;
UPROPERTY() int32 Count = 0;
};
FInventoryItem a;
FInventoryItem b = a; // โ
๋ณต์ฌ โ UStruct ์๋๋ ๋์
๋ณต์ฌ๋ฅผ ๋ง์ผ๋ ค๋ฉด ์ผ๋ฐ C++ ๊ท์น๋๋ก = delete:
1
2
3
4
5
6
7
8
USTRUCT()
struct FBigData
{
GENERATED_BODY()
FBigData() = default;
FBigData(const FBigData&) = delete;
FBigData& operator=(const FBigData&) = delete;
};
๋น๊ต ํ
| ย | std | ์ธ๋ฆฌ์ผ |
|---|---|---|
| ๋ณต์ฌ ์ฐจ๋จ ๋งคํฌ๋ก | ์ง์ = delete | UE_NONCOPYABLE(T) |
| noncopyable ๋ฒ ์ด์ค | boost::noncopyable | FNoncopyable |
| move-only ์ค๋งํธ ํฌ์ธํฐ | unique_ptr | TUniquePtr (MoveTemp) |
| ๊ณต์ ์์ | shared_ptr | TSharedPtr / TSharedRef |
| ๋ณต์ฌ ๋ถ๊ฐ ๊ฐ์ฒด | ์ง์ noncopyable ์์ฑ | UObject (์๋) |
| ์ฌ๋ผ์ด์ฑ ์ฐจ๋จ | virtual ์๋ฉธ์ + delete | UObject ๋ณธ์ง + virtual |
์ฐธ๊ณ
- 11_smart_pointer.md โ
unique_ptr(move-only)ยทshared_ptr(๋ณต์ฌ ๊ฐ๋ฅ) ์์ ๊ถ ๋ชจ๋ธ, ์ค๋ ํ๊ณ 1ยท4๋ฒ ์ฐ๊ฒฐ - 09_rtti_raii.md โ RAII ์์ ๊ด๋ฆฌ, Rule of Zero์ ์ด์์ ํ์ฉ
- 06_virtual_destructor.md โ ๋คํ ๊ธฐ๋ฐ ํด๋์ค + ์ฌ๋ผ์ด์ฑ ์ฐจ๋จ + virtual ์๋ฉธ์ ํตํฉ ํจํด
- 08_vtable_deepdive.md โ ์ฌ๋ผ์ด์ฑ ์ vptr์ด Base๋ก ๊ณ ์ ๋๋ ์ด์