๐ 04/12 - OOP (๊ฐ์ฒด์งํฅ ํ๋ก๊ทธ๋๋ฐ) ์ ๋ฆฌ
Notion ์๋ณธ: https://www.notion.so/340f77b24d2f81808ae2d58e64d8dc2e ๋ถ๋ชจ: ์๋ฃ โ Cs ๋ฉด์ ์ค๋น โ cs ์ฃผ์
๋ชจ์๋ฉด์ ๋ต๋ณ โ โOOP๋ ๋ฌด์์ธ๊ฐ์?โ
OOP, ์ฆ ๊ฐ์ฒด์งํฅ ํ๋ก๊ทธ๋๋ฐ์ ํ๋ก๊ทธ๋จ์ ๊ฐ์ฒด(Object) ๋จ์๋ก ๊ตฌ์ฑํ๋ ํ๋ก๊ทธ๋๋ฐ ํจ๋ฌ๋ค์์
๋๋ค. ํ์ค ์ธ๊ณ์ ์ฌ๋ฌผ์ฒ๋ผ, ๋ฐ์ดํฐ(์์ฑ)์ ํ๋(๋ฉ์๋)์ ํ๋์ ๊ฐ์ฒด๋ก ๋ฌถ์ด ๊ด๋ฆฌํ๋ฉฐ, ํด๋์ค๋ ์ค๊ณ๋, ๊ฐ์ฒด๋ ๊ทธ ์ค๊ณ๋๋ก ๋ง๋ ์ค์ฒด์
๋๋ค.
OOP๋ 4๊ฐ์ง ํต์ฌ ์์น์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
- ์ฒซ์งธ, ์บก์ํ์
๋๋ค. ๋ฐ์ดํฐ์ ๋ฉ์๋๋ฅผ ํ๋์ ํด๋์ค๋ก ๋ฌถ๊ณ , private ์ ๊ทผ ์ง์ ์๋ก ๋ด๋ถ ๊ตฌํ์ ์จ๊ฒจ ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ์ ๋ณด์ฅํฉ๋๋ค.
- ๋์งธ, ์์์
๋๋ค. ๋ถ๋ชจ ํด๋์ค์ ์์ฑ๊ณผ ๋ฉ์๋๋ฅผ ์์ ํด๋์ค๊ฐ ๋ฌผ๋ ค๋ฐ์ ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ์ ๋์
๋๋ค.
- ์
์งธ, ๋คํ์ฑ์
๋๋ค. ๊ฐ์ ์ธํฐํ์ด์ค๋ก ๋ค๋ฅธ ๋์์ ์ํํ๋ ๋ฅ๋ ฅ์ผ๋ก, C++์์๋ ๊ฐ์ ํจ์(virtual)์ ์ค๋ฒ๋ผ์ด๋ฉ์ด ์์ต๋๋ค.
- ๋ท์งธ, ์ถ์ํ์
๋๋ค. ๋ณต์กํ ๊ตฌํ ์ธ๋ถ์ฌํญ์ ์จ๊ธฐ๊ณ ํต์ฌ ์ธํฐํ์ด์ค๋ง ์ธ๋ถ์ ๋
ธ์ถํฉ๋๋ค. C++์์๋ ์์ ๊ฐ์ ํจ์(= 0)๋ก ์ถ์ ํด๋์ค๋ฅผ ๊ตฌํํฉ๋๋ค.
์ค์ ์์๋ก, ์ธ๋ฆฌ์ผ ์์ง์ AActor๋ UObject ๋ชจ๋ ์ด OOP ์์น์ ๊ธฐ๋ฐ์ผ๋ก ์ค๊ณ๋์ด ์์ด, BeginPlay()์ Tick() ๊ฐ์ ๊ฐ์ ํจ์๋ฅผ ์ฌ์ ์ํด ๋คํ์ฑ์ ํ์ฉํฉ๋๋ค.
๊ธฐ๋ณธ ๊ฐ๋
- ๊ฐ์ฒด (Object) โ ๋ฐ์ดํฐ(์์ฑ)์ ํ๋(๋ฉ์๋)์ ๋ฌถ์ ๋จ์
- ํด๋์ค (Class) โ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ธฐ ์ํ ์ค๊ณ๋ (๋ถ์ด๋นต ํ)
- ์ธ์คํด์ค (Instance) โ ํด๋์ค์์ ์์ฑ๋ ์ค์ฒด (๋ถ์ด๋นต)
- ํจ๋ฌ๋ค์ (Paradigm) โ ํ๋ก๊ทธ๋๋ฐ์ ๋ฐ๋ผ๋ณด๋ ๋ฐฉ์/๊ด์
- ์์ฑ (Attribute) โ ๊ฐ์ฒด๊ฐ ๊ฐ์ง๋ ๋ฐ์ดํฐ (๋ฉค๋ฒ ๋ณ์)
- ๋ฉ์๋ (Method) โ ๊ฐ์ฒด๊ฐ ์ํํ๋ ํ๋ (๋ฉค๋ฒ ํจ์)
4๋ ์์น
| ์์น | ํต์ฌ ํค์๋ |
|---|
| ์บก์ํ (Encapsulation) | private, ์ ๊ทผ ์ง์ ์, ์ ๋ณด ์๋, ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ, Getter/Setter |
| ์์ (Inheritance) | IS-A ๊ด๊ณ, ๋ถ๋ชจ/์์ ํด๋์ค, ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ, virtual, override |
| ๋คํ์ฑ (Polymorphism) | ๊ฐ์ ํจ์(virtual), ์ค๋ฒ๋ผ์ด๋ฉ, ๋ฐํ์ ๊ฒฐ์ , vtable, ๋์ ๋์คํจ์น |
| ์ถ์ํ (Abstraction) | ์์ ๊ฐ์ ํจ์(= 0), ์ถ์ ํด๋์ค, ์ธํฐํ์ด์ค, ๊ณ์ฝ(Contract) |
์ธ๋ฆฌ์ผ ์ฐ๊ฒฐ ํค์๋
AActor / UObject โ ์ธ๋ฆฌ์ผ์ ํต์ฌ ํด๋์ค, OOP ๊ธฐ๋ฐ ์ค๊ณBeginPlay() / Tick() โ ๊ฐ์ ํจ์ โ ๋คํ์ฑ ์ค์ ํ์ฉ ์์UPROPERTY() โ ์บก์ํ์ ์ฐ์ฅ์ (์๋ํฐ ๋
ธ์ถ ๋ฒ์ ์ ์ด)IInterface โ ์ธ๋ฆฌ์ผ์ ์ธํฐํ์ด์ค ํจํด (์ถ์ํ)
new๋ ํจ์์ธ๊ฐ?
new๋ ํจ์๊ฐ ์๋๋ผ C++ ์ฐ์ฐ์(operator)์
๋๋ค. ๋จ, ๋ด๋ถ์ ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ ์ํด operator new๋ผ๋ ํจ์๋ฅผ ํธ์ถํฉ๋๋ค. operator new๋ ์ค๋ฒ๋ก๋ฉ ๊ฐ๋ฅํ ํจ์์ด๊ณ , new ํํ์์ ๊ทธ๊ฒ์ ๊ฐ์ธ๋ ์ฐ์ฐ์์
๋๋ค.
new ํํ์์ ์คํ ๋จ๊ณ
1
2
3
4
5
| Player* p = new Player();
// ๋ด๋ถ์ ์ผ๋ก:
void* mem = operator new(sizeof(Player)); // 1. ๋ฉ๋ชจ๋ฆฌ ํ ๋น
new(mem) Player(); // 2. ์์ฑ์ ํธ์ถ (placement new)
Player* p = static_cast<Player*>(mem); // 3. ํ์
ํฌ์ธํฐ๋ก ๋ฐํ
|
operator new vs new ํํ์
| ย | new ํํ์ | operator new |
|---|
| ๋ถ๋ฅ | ์ฐ์ฐ์ | ํจ์ |
| ์ญํ | ๋ฉ๋ชจ๋ฆฌ ํ ๋น + ์์ฑ์ ํธ์ถ | ๋ฉ๋ชจ๋ฆฌ ํ ๋น๋ง |
| ์ค๋ฒ๋ก๋ฉ | ๋ถ๊ฐ | ๊ฐ๋ฅ |
| ์ง์ ํธ์ถ | new T() | operator new(sizeof(T)) |
๊ฐ์ฒด(Object)๋?
1
2
3
4
5
6
7
8
9
10
11
12
13
| // ํด๋์ค = ์ค๊ณ๋ (๋ถ์ด๋นต ํ)
class Player {
public:
string name; // ์์ฑ
int health;
void Attack() { } // ํ๋
void Move() { }
};
// ๊ฐ์ฒด = ์ธ์คํด์ค (๋ถ์ด๋นต)
Player p1;
Player p2; // p2๋ ๋ณ๊ฐ์ ๊ฐ์ฒด โ ๊ฐ์ ์ค๊ณ๋, ๋ค๋ฅธ ์ค์ฒด
|
์บก์ํ (Encapsulation)
์บก์ํ๋ ๋ฐ์ดํฐ(๋ฉค๋ฒ ๋ณ์)์ ๋ฉ์๋(๋ฉค๋ฒ ํจ์)๋ฅผ ํ๋์ ํด๋์ค๋ก ๋ฌถ๊ณ , ๋ด๋ถ ๊ตฌํ์ ์ธ๋ถ์์ ์ง์ ์ ๊ทผํ์ง ๋ชปํ๋๋ก ์ ๋ณด ์๋(Information Hiding)ํ๋ ๊ฒ์
๋๋ค. ์ ๊ทผ ์ง์ ์(public / protected / private)๋ก ์ธ๋ถ ๋
ธ์ถ ๋ฒ์๋ฅผ ์ ์ดํฉ๋๋ค.
์ ๊ทผ ์ง์ ์
- public: ํด๋์ค ๋ด๋ถ + ์ธ๋ถ + ํ์ ํด๋์ค โ ์ธ๋ถ ์ธํฐํ์ด์ค
- protected: ํด๋์ค ๋ด๋ถ + ํ์ ํด๋์ค โ ํ์ ํด๋์ค ์ ์ฉ
- private: ํด๋์ค ๋ด๋ถ๋ง โ ๊ตฌํ ์ธ๋ถ์ฌํญ ์๋
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| class BankAccount {
private:
double balance;
public:
void Deposit(double amount) {
if (amount > 0)
balance += amount;
}
void Withdraw(double amount) {
if (amount > 0 && amount <= balance)
balance -= amount;
}
double GetBalance() const { return balance; }
};
BankAccount acc;
// acc.balance = -500; // ERROR
acc.Deposit(1000); // OK โ ๊ฒ์ฆ ํ ์์ ์ฒ๋ฆฌ
|
์์ (Inheritance)
์์์ ๊ธฐ์กด ํด๋์ค(๋ถ๋ชจ/๊ธฐ๋ฐ ํด๋์ค)์ ์์ฑ๊ณผ ๋ฉ์๋๋ฅผ ์ ํด๋์ค(์์/ํ์ ํด๋์ค)๊ฐ ๋ฌผ๋ ค๋ฐ์ ์ฌ์ฌ์ฉํ๋ ๋ฉ์ปค๋์ฆ์
๋๋ค. ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด๊ณ , IS-A ๊ด๊ณ๋ฅผ ํํํฉ๋๋ค.
IS-A vs HAS-A
- IS-A: public ์์ (Dog IS-A Animal)
- HAS-A: ์ปดํฌ์ง์
or private ์์ (Car HAS-A Engine)
์์ ๋ฐฉ์๋ณ ์ ๊ทผ ์ง์ ์ ๋ณํ
| ๊ธฐ๋ณธ ํด๋์ค ๋ฉค๋ฒ | public ์์ | protected ์์ | private ์์ |
|---|
| public | โ public | โ protected | โ private |
| protected | โ protected | โ protected | โ private |
| private | ์ ๊ทผ ๋ถ๊ฐ | ์ ๊ทผ ๋ถ๊ฐ | ์ ๊ทผ ๋ถ๊ฐ |
๊ฐ์ ํจ์ + override
1
2
3
4
5
6
7
8
9
10
| class Shape {
public:
virtual void Draw() = 0; // ์์ ๊ฐ์ ํจ์
virtual ~Shape() { } // ๊ฐ์ ์๋ฉธ์ ํ์!
};
class Circle : public Shape {
public:
void Draw() override { cout << "์ ๊ทธ๋ฆฌ๊ธฐ\n"; }
};
|
๋ณต๊ธฐ:
- IS-A = public ์์, HAS-A = ์ปดํฌ์ง์
virtual ์์ผ๋ฉด ํ์ ํด๋์ค์์ ์ฌ์ ์ํด๋ ๋คํ์ฑ ๋ถ๊ฐ- ๊ฐ์ ์๋ฉธ์ ์์ผ๋ฉด ํ์ ํด๋์ค ์๋ฉธ์ ๋ฏธํธ์ถ โ ๋ฉ๋ชจ๋ฆฌ ๋์
override ํค์๋๋ก ์ฌ์ ์ ๋ช
์ โ ์คํ๋ฅผ ์ปดํ์ผ ์๋ฌ๋ก
๋คํ์ฑ (Polymorphism)
๋คํ์ฑ์ ๊ฐ์ ์ธํฐํ์ด์ค(ํจ์ ์ด๋ฆ)๋ก ๋ค๋ฅธ ๋์์ ์ํํ๋ ๋ฅ๋ ฅ์
๋๋ค. C++์์๋ ์ปดํ์ผ ํ์ ๋คํ์ฑ(์ค๋ฒ๋ก๋ฉ, ํ
ํ๋ฆฟ)๊ณผ ๋ฐํ์ ๋คํ์ฑ(๊ฐ์ ํจ์, ์ค๋ฒ๋ผ์ด๋ฉ)์ผ๋ก ๋๋ฉ๋๋ค.
์ปดํ์ผ ํ์ vs ๋ฐํ์
| ย | ์ปดํ์ผ ํ์ | ๋ฐํ์ |
|---|
| ๊ตฌํ ๋ฐฉ๋ฒ | ํจ์ ์ค๋ฒ๋ก๋ฉ, ํ
ํ๋ฆฟ | ๊ฐ์ ํจ์, ์ค๋ฒ๋ผ์ด๋ฉ |
| ๊ฒฐ์ ์์ | ์ปดํ์ผ ์ | ๋ฐํ์ ์ (vtable) |
| ์ฑ๋ฅ | ๋น ๋ฆ (์ธ๋ผ์ธ ๊ฐ๋ฅ) | vtable ์ฐธ์กฐ ๋น์ฉ ๋ฐ์ |
| ์ ์ฐ์ฑ | ๋ฎ์ | ๋์ (๋์ ๋์คํจ์น) |
ํจ์ ์ค๋ฒ๋ก๋ฉ (์ปดํ์ผ ํ์)
1
2
3
4
5
6
7
| void Attack(int damage) { cout << "๋ฌผ๋ฆฌ ๊ณต๊ฒฉ: " << damage; }
void Attack(float damage) { cout << "๋ง๋ฒ ๊ณต๊ฒฉ: " << damage; }
void Attack(int damage, int cnt) { cout << "์ฐ์ ๊ณต๊ฒฉ: " << damage * cnt; }
Attack(50); // Attack(int)
Attack(30.5f); // Attack(float)
Attack(20, 3); // Attack(int, int)
|
๊ฐ์ ํจ์ (๋ฐํ์)
1
2
3
4
5
6
7
8
9
10
11
12
| class Shape {
public:
virtual void Draw() { cout << "๋ํ\n"; }
virtual ~Shape() { }
};
class Circle : public Shape { public: void Draw() override { cout << "์\n"; } };
class Triangle : public Shape { public: void Draw() override { cout << "์ผ๊ฐํ\n"; } };
Shape* shapes[] = { new Circle(), new Triangle() };
for (Shape* s : shapes)
s->Draw(); // "์" โ "์ผ๊ฐํ"
|
vtable ๋์
- ๊ฐ์ ํจ์ ์์ผ๋ฉด ์ปดํ์ผ๋ฌ๊ฐ vtable ์๋ ์์ฑ
- ๊ฐ์ฒด๋ vptr(vtable ํฌ์ธํฐ)์ ์จ๊ฒจ์ ๋ณด์
- s->Draw() ํธ์ถ ์: vptr ์ฝ๊ธฐ โ vtable์์ ํจ์ ํฌ์ธํฐ ์ฐพ๊ธฐ โ ๊ฐ์ ํธ์ถ
- ์ด๊ฒ์ด ๋์ ๋์คํจ์น(Dynamic Dispatch)
์ถ์ํ (Abstraction)
์ถ์ํ๋ ๋ณต์กํ ๊ตฌํ ์ธ๋ถ์ฌํญ์ ์จ๊ธฐ๊ณ ํต์ฌ ์ธํฐํ์ด์ค๋ง ์ธ๋ถ์ ๋
ธ์ถํ๋ ๊ฒ์
๋๋ค. C++์์๋ ์์ ๊ฐ์ ํจ์(= 0)๋ก ์ถ์ ํด๋์ค๋ฅผ ๋ง๋ค์ด ๊ตฌํํฉ๋๋ค.
์ถ์ ํด๋์ค vs ์ธํฐํ์ด์ค
| ย | ์ถ์ ํด๋์ค | ์ธํฐํ์ด์ค |
|---|
| C++ ๊ตฌํ | ์์ ๊ฐ์ ํจ์ 1๊ฐ ์ด์ | ๋ชจ๋ ํจ์๊ฐ ์์ ๊ฐ์ ํจ์ |
| ๋ฉค๋ฒ ๋ณ์ | ๊ฐ์ง ์ ์์ | ์์ (๊ถ์ฅ) |
| ์ธ์คํด์คํ | ๋ถ๊ฐ | ๋ถ๊ฐ |
| ์ฉ๋ | ๊ณตํต ๊ตฌํ + ์ผ๋ถ ์ถ์ํ | ๊ณ์ฝ(Contract)๋ง ์ ์ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| class Animal {
public:
virtual void MakeSound() = 0; // ์์ ๊ฐ์ ํจ์
virtual ~Animal() { }
void Breathe() { cout << "์จ์ฌ๊ธฐ\n"; }
};
class Dog : public Animal {
public:
void MakeSound() override { cout << "๋ฉ๋ฉ!\n"; }
};
// Animal a; // ERROR โ ์ถ์ ํด๋์ค๋ ์ธ์คํด์คํ ๋ถ๊ฐ
Dog d;
d.MakeSound(); // "๋ฉ๋ฉ!"
|
๋ค์ค ์ธํฐํ์ด์ค ๊ตฌํ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| class IAttackable {
public:
virtual void Attack() = 0;
virtual int GetDamage() const = 0;
virtual ~IAttackable() { }
};
class IMovable {
public:
virtual void Move(float x, float y) = 0;
virtual ~IMovable() { }
};
class Player : public IAttackable, public IMovable {
public:
void Attack() override { }
int GetDamage() const override { return 50; }
void Move(float x, float y) override { }
};
|
OOP vs ์ ์ฐจ์งํฅ ๋น๊ต
| ํญ๋ชฉ | ์ ์ฐจ์งํฅ | OOP |
|---|
| ๊ตฌ์กฐ ๋จ์ | ํจ์ | ๊ฐ์ฒด |
| ๋ฐ์ดํฐ ์ ๊ทผ | ์ ์ญ/๊ณต์ ์ง์ ์ ๊ทผ | ์บก์ํ๋ก ๋ณดํธ |
| ์ฝ๋ ์ฌ์ฌ์ฉ | ํจ์ ์ฌ์ฌ์ฉ | ์์ยท์ปดํฌ์ง์
|
| ์ ์ง๋ณด์ | ๊ท๋ชจ ์ปค์ง์๋ก ์ด๋ ค์ | ์บก์ํ๋ก ๋ณ๊ฒฝ ์ํฅ ์ ํ |
| ์ธ์ด ์์ | C, Pascal, FORTRAN | C++, Java, Python, C# |
| ์ ํฉ | ๋จ์ ์คํฌ๋ฆฝํธ | ๋๊ท๋ชจ ์์คํ
, ๊ฒ์ ์์ง |
SOLID ์์น
SOLID๋ ์ ์ง๋ณด์ ๊ฐ๋ฅํ OOP ์ค๊ณ๋ฅผ ์ํ 5๊ฐ์ง ์์น์
๋๋ค. ๋ก๋ฒํธ C. ๋งํด์ด ์ ๋ฆฌํ์์ต๋๋ค.
| ์์น | ์ด๋ฆ | ํต์ฌ ๋ด์ฉ |
|---|
| S | ๋จ์ผ ์ฑ
์ ์์น (SRP) | ํด๋์ค๋ ํ๋์ ์ฑ
์๋ง ๊ฐ์ ธ์ผ ํ๋ค |
| O | ๊ฐ๋ฐฉ-ํ์ ์์น (OCP) | ํ์ฅ์๋ ์ด๋ ค์๊ณ , ์์ ์๋ ๋ซํ์์ด์ผ ํ๋ค |
| L | ๋ฆฌ์ค์ฝํ ์นํ ์์น (LSP) | ์์ ํด๋์ค๋ ๋ถ๋ชจ ํด๋์ค๋ก ๋์ฒด ๊ฐ๋ฅํด์ผ ํ๋ค |
| I | ์ธํฐํ์ด์ค ๋ถ๋ฆฌ ์์น (ISP) | ํด๋ผ์ด์ธํธ๋ ์ฌ์ฉํ์ง ์๋ ์ธํฐํ์ด์ค์ ์์กดํ์ง ์์์ผ ํ๋ค |
| D | ์์กด์ฑ ์ญ์ ์์น (DIP) | ๊ณ ์์ค ๋ชจ๋์ ์ ์์ค ๋ชจ๋์ด ์๋ ์ถ์ํ์ ์์กดํด์ผ ํ๋ค |
SRP
1
2
3
4
5
6
7
8
9
10
11
| // ๋์ ์
class Player {
void Move() { }
void SaveData() { } // ๋ค๋ฅธ ์ฑ
์!
void Render() { } // ๋ ๋ค๋ฅธ ์ฑ
์!
};
// ์ข์ ์
class Player { void Move() { } };
class PlayerSaver { void Save(const Player& p) { } };
class PlayerRenderer { void Render(const Player& p) { } };
|
OCP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // ๋์ ์ โ ์ ๋ฌด๊ธฐ ์ถ๊ฐ ์ ๊ธฐ์กด ์ฝ๋ ์์
void UseWeapon(string type) {
if (type == "sword") { }
else if (type == "bow") { }
}
// ์ข์ ์ โ ์ถ์ํ๋ก ํ์ฅ ๊ฐ๋ฅ
class IWeapon {
public:
virtual void Use() = 0;
virtual ~IWeapon() { }
};
class Sword : public IWeapon { public: void Use() override { } };
class Bow : public IWeapon { public: void Use() override { } };
|
๋ณต๊ธฐ:
- SRP: ํด๋์ค ํ๋ = ์ฑ
์ ํ๋
- OCP: ๊ธฐ์กด ์ฝ๋ ์์ ์์ด ์ ๊ธฐ๋ฅ ์ถ๊ฐ (์ถ์ํ ํ์ฉ)
- LSP: ๋ถ๋ชจ ํ์
ํฌ์ธํฐ๋ก ์์ ์ฌ์ฉ ์ ๋์์ด ์์๊ณผ ๊ฐ์์ผ ํจ
- ISP: ๋ฑ๋ฑํ ์ธํฐํ์ด์ค โ ์์ ์ธํฐํ์ด์ค ์ฌ๋ฌ ๊ฐ๋ก ๋ถ๋ฆฌ
- DIP: ๊ตฌ์ฒด ํด๋์ค๊ฐ ์๋ ์ธํฐํ์ด์ค(์ถ์ํ)์ ์์กด
- ์ธ๋ฆฌ์ผ:
IInterface, UActorComponent ๋ถ๋ฆฌ ์ค๊ณ = SOLID ์ค์ฒ