ํฌ์ŠคํŠธ

CS โ€” class vs struct

CS โ€” class vs struct

๐Ÿ“• class์™€ struct ์˜ ์ฐจ์ด์ 

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

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

C++์—์„œ class์™€ struct์˜ ๊ธฐ๋Šฅ์  ์ฐจ์ด๋Š” ๋”ฑ ๋‘ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ์ฒซ์งธ, ๊ธฐ๋ณธ ์ ‘๊ทผ ์ง€์ •์ž๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. struct๋Š” public์ด ๊ธฐ๋ณธ์ด๊ณ , class๋Š” private์ด ๊ธฐ๋ณธ์ž…๋‹ˆ๋‹ค. ๋‘˜์งธ, ๊ธฐ๋ณธ ์ƒ์† ๋ฐฉ์‹์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค. struct๋Š” public ์ƒ์†, class๋Š” private ์ƒ์†์ด ๊ธฐ๋ณธ์ž…๋‹ˆ๋‹ค. ์ด ์™ธ์— ๋ฉค๋ฒ„ ํ•จ์ˆ˜, ์ƒ์„ฑ์ž, ์†Œ๋ฉธ์ž, ์ƒ์†, ํ…œํ”Œ๋ฆฟ ๋“ฑ ๋ชจ๋“  ๊ธฐ๋Šฅ์€ ์™„์ „ํžˆ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ๊ด€์šฉ์ ์œผ๋กœ๋Š” ๋ฐ์ดํ„ฐ๋งŒ ๋ฌถ๋Š” ๋‹จ์ˆœ ์ง‘ํ•ฉ์ฒด์—๋Š” struct๋ฅผ, ์บก์Аํ™”์™€ ์ •๋ณด ์€๋‹‰์ด ํ•„์š”ํ•œ ํƒ€์ž…์—๋Š” class๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ถ”๊ฐ€๋กœ ํ…œํ”Œ๋ฆฟ ๋งค๊ฐœ๋ณ€์ˆ˜์—์„œ๋Š” class๋‚˜ typename์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, struct๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์ฐจ์ด๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

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

  • ๊ธฐ๋ณธ ์ ‘๊ทผ ์ง€์ •์ž (Default Access Specifier) โ€” struct๋Š” public, class๋Š” private์ด ๊ธฐ๋ณธ
  • ๊ธฐ๋ณธ ์ƒ์† ๋ฐฉ์‹ (Default Inheritance) โ€” struct๋Š” public ์ƒ์†, class๋Š” private ์ƒ์†์ด ๊ธฐ๋ณธ
  • ์บก์Аํ™” (Encapsulation) โ€” ๋ฐ์ดํ„ฐ์™€ ํ•จ์ˆ˜๋ฅผ ํ•˜๋‚˜๋กœ ๋ฌถ๊ณ  ์™ธ๋ถ€ ์ ‘๊ทผ์„ ์ œ์–ดํ•˜๋Š” OOP ์›์น™
  • ์ •๋ณด ์€๋‹‰ (Information Hiding) โ€” ๋‚ด๋ถ€ ๊ตฌํ˜„์„ ์ˆจ๊ธฐ๊ณ  public ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ๋…ธ์ถœ
  • POD (Plain Old Data) โ€” ๋‹จ์ˆœ ๋ฐ์ดํ„ฐ ์ง‘ํ•ฉ์ฒด. Trivial + Standard-layout ์กฐ๊ฑด ์ถฉ์กฑ ํ•„์š”
  • ํ…œํ”Œ๋ฆฟ ๋งค๊ฐœ๋ณ€์ˆ˜ (Template Parameter) โ€” template<class T>, template<typename T>๋Š” ๊ฐ€๋Šฅํ•˜์ง€๋งŒ template<struct T>๋Š” ์ปดํŒŒ์ผ ์—๋Ÿฌ

์ƒ์„ธ ๋น„๊ต

๊ธฐ๋ณธ ์ ‘๊ทผ ์ง€์ •์ž

1
2
3
4
5
6
7
8
9
struct MyStruct {
    int x;        // public (๊ธฐ๋ณธ๊ฐ’)
    void foo() {}
};

class MyClass {
    int x;        // private (๊ธฐ๋ณธ๊ฐ’)
    void foo() {}
};

๊ธฐ๋ณธ ์ƒ์† ๋ฐฉ์‹

1
2
3
4
5
struct DerivedS : Base { };   // public ์ƒ์† (๊ธฐ๋ณธ)
class  DerivedC : Base { };   // private ์ƒ์† (๊ธฐ๋ณธ)

struct S : public Base { };
class  C : public Base { };

๊ด€์šฉ์  ์‚ฌ์šฉ ๊ตฌ๋ถ„

  • struct: ๋ฐ์ดํ„ฐ๋งŒ ๋ฌถ๋Š” ๋‹จ์ˆœ ์ง‘ํ•ฉ์ฒด (POD), ์ขŒํ‘œยท์ƒ‰์ƒยท์„ค์ •๊ฐ’ ๋ฌถ์Œ
  • class: ์บก์Аํ™”ยท์ •๋ณด ์€๋‹‰์ด ํ•„์š”ํ•œ ํƒ€์ž…, PlayerยทItemยทManager ๋“ฑ

C vs C++: C์˜ struct๋Š” ํ•จ์ˆ˜ ๋ฉค๋ฒ„ ๋ถˆ๊ฐ€. C++์—์„œ๋Š” struct์—๋„ ์ƒ์„ฑ์žยท์†Œ๋ฉธ์žยท๋ฉค๋ฒ„ ํ•จ์ˆ˜ ๋ชจ๋‘ ๊ฐ€๋Šฅ

ํ…œํ”Œ๋ฆฟ ๋งค๊ฐœ๋ณ€์ˆ˜์—์„œ์˜ ์ฐจ์ด

1
2
3
template <class T>    // OK
template <typename T> // OK
template <struct T>   // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ

์™œ struct๋Š” ํ…œํ”Œ๋ฆฟ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š”๊ฐ€:

  1. C++ ํ‘œ์ค€ ๋ช…์„ธ [temp.param]์€ class identifier ๋˜๋Š” typename identifier ๋‘ ํ˜•ํƒœ๋กœ๋งŒ ์ •์˜
  2. ์—ญ์‚ฌ์  ๋ฐฐ๊ฒฝ โ€” ์ดˆ๊ธฐ C++๋Š” class๋งŒ, C++98์—์„œ typename ์ถ”๊ฐ€ (์˜๋ฏธ์  ๋ณด์™„), struct๋Š” ์ถ”๊ฐ€ ๋™๊ธฐ ์—†์Œ
  3. ์˜๋ฏธ์  ์ค‘๋ณต ํšŒํ”ผ โ€” ํ…œํ”Œ๋ฆฟ ๋งค๊ฐœ๋ณ€์ˆ˜์—์„œ class๋Š” โ€œ์ž„์˜์˜ ํƒ€์ž…โ€์ด๋ผ ์ ‘๊ทผ ์ง€์ •์ž์™€ ๋ฌด๊ด€
  4. ์–ธ์–ด ์ผ๊ด€์„ฑ โ€” class์™€ typename๋งŒ์œผ๋กœ ์ถฉ๋ถ„

Aggregate ์ดˆ๊ธฐํ™” (C++17~)

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Point { int x; int y; };   // aggregate
Point p = {10, 20};                // OK

class Pos {
public:
    int x; int y;
};
Pos q = {10, 20};                  // OK

class Hidden {
    int x; int y;                  // private โ†’ aggregate ์•„๋‹˜
};
// Hidden h = {10, 20};            // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ

๊ผฌ๋ฆฌ๋ฌผ๊ธฐ ์˜ˆ์ƒ ์งˆ๋ฌธ

Q1. struct์— ์ƒ์„ฑ์ž๋ฅผ ์ •์˜ํ•˜๋ฉด POD๊ฐ€ ๋˜๋‚˜์š”?

์•„๋‹ˆ์š”. ์‚ฌ์šฉ์ž ์ •์˜ ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์œผ๋ฉด trivial ์ƒ์„ฑ์ž ์กฐ๊ฑด์ด ๊นจ์ ธ POD๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. POD = Trivial + Standard-layout ๋‘ ์กฐ๊ฑด์„ ๋ชจ๋‘ ์ถฉ์กฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

1
2
struct A { A() {} };           // ์‚ฌ์šฉ์ž ์ •์˜ ์ƒ์„ฑ์ž โ†’ trivial ์•„๋‹˜ โ†’ POD ์•„๋‹˜
struct B { int x; int y; };    // ๊ธฐ๋ณธ ์ƒ์„ฑ์ž โ†’ trivial โ†’ POD

Q2. struct์™€ class์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ ˆ์ด์•„์›ƒ์ด ๋‹ค๋ฅธ๊ฐ€์š”?

๊ธฐ๋ณธ์ ์œผ๋กœ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ํŒจ๋”ฉ์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋‹จ, ์ ‘๊ทผ ์ง€์ •์ž๊ฐ€ ๋‹ค๋ฅธ ๋ฉค๋ฒ„ ์‚ฌ์ด์˜ ์ˆœ์„œ๋Š” ํ‘œ์ค€์—์„œ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฐ™์€ ์ ‘๊ทผ ์ง€์ •์ž ๋‚ด์—์„œ๋Š” ์„ ์–ธ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค.

Q3. union๊ณผ struct์˜ ์ฐจ์ด๋Š”?

  • struct: ๊ฐ ๋ฉค๋ฒ„๊ฐ€ ๋ณ„๋„์˜ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น โ†’ ํฌ๊ธฐ = ๋ชจ๋“  ๋ฉค๋ฒ„ ํ•ฉ + ํŒจ๋”ฉ
  • union: ๋ชจ๋“  ๋ฉค๋ฒ„๊ฐ€ ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณต์œ  โ†’ ํฌ๊ธฐ = ๊ฐ€์žฅ ํฐ ๋ฉค๋ฒ„
  • union์€ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ๋ฉค๋ฒ„๋งŒ ์œ ํšจ (๋™์‹œ ์ ‘๊ทผ ์‹œ UB)

Q4. C++ Core Guidelines์—์„œ struct์™€ class ์‚ฌ์šฉ ๊ธฐ์ค€์€?

๋ถˆ๋ณ€์‹(invariant)์ด ์—†๋Š” ๋‹จ์ˆœ ๋ฐ์ดํ„ฐ ๋ฌถ์Œ์€ struct๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

  • struct ๊ถŒ์žฅ: ๋ชจ๋“  ๋ฉค๋ฒ„๊ฐ€ ํ•ญ์ƒ ์œ ํšจํ•œ ์ƒํƒœ์ผ ๋•Œ (์ขŒํ‘œ, ์ƒ‰์ƒ๊ฐ’)
  • class ๊ถŒ์žฅ: ๋ฉค๋ฒ„ ๊ฐ„ ์˜์กด์„ฑ์ด๋‚˜ ์œ ํšจ์„ฑ ์กฐ๊ฑด์ด ์žˆ์„ ๋•Œ

Q5. ์–ธ๋ฆฌ์–ผ ์—”์ง„์—์„œ USTRUCT์™€ UCLASS์˜ ์ฐจ์ด๋Š”?

  • USTRUCT: GC ๋ฏธ์ง€์›, UPROPERTY๋งŒ ๋ฆฌํ”Œ๋ ‰์…˜, ๋ธ”๋ฃจํ”„๋ฆฐํŠธ ์ œํ•œ์ , ๋‹จ์ผ ์ƒ์†, ๊ฒฝ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ปจํ…Œ์ด๋„ˆ (FVector ๋“ฑ)
  • UCLASS: UObject GC ๊ด€๋ฆฌ, UPROPERTY + UFUNCTION ์ „์ฒด ๋ฆฌํ”Œ๋ ‰์…˜, ๋ธ”๋ฃจํ”„๋ฆฐํŠธ ์™„์ „ ์ง€์›, UObject ๊ณ„์ธต ์ƒ์†, ๊ฒŒ์ž„ ๊ฐ์ฒด (AActor, UComponent)
1
2
3
4
5
6
7
8
9
10
11
12
13
USTRUCT(BlueprintType)
struct FDamageInfo {
    GENERATED_BODY()
    UPROPERTY() float Damage;
    UPROPERTY() AActor* Instigator;
};

UCLASS()
class AMyCharacter : public ACharacter {
    GENERATED_BODY()
    UFUNCTION(BlueprintCallable)
    void TakeDamage(FDamageInfo Info);
};

Q5-1. UCLASS์™€ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ(GC)์˜ ์—ฐ๊ฒฐ

์–ธ๋ฆฌ์–ผ์˜ GC๋Š” C++ ํ‘œ์ค€ GC๊ฐ€ ์•„๋‹ˆ๋ผ ์—”์ง„ ์ž์ฒด ๊ตฌํ˜„์ž…๋‹ˆ๋‹ค. UObject๋ฅผ ์ƒ์†ํ•œ UCLASS๋งŒ GC ๋Œ€์ƒ์ด ๋ฉ๋‹ˆ๋‹ค.

GC ๋™์ž‘ ์›๋ฆฌ (Mark & Sweep):

  1. Mark โ€” ๋ฃจํŠธ ์˜ค๋ธŒ์ ํŠธ์—์„œ ์ถœ๋ฐœ, UPROPERTY๋กœ ์ฐธ์กฐ๋œ UObject๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํƒ์ƒ‰
  2. Sweep โ€” ํ‘œ์‹œ๋˜์ง€ ์•Š์€ UObject๋ฅผ ํŒŒ๊ดด
  3. ์ฃผ๊ธฐ โ€” ๊ธฐ๋ณธ 60์ดˆ (gc.TimeBetweenPurgingPendingKillObjects)

GC Root๊ฐ€ ๋˜๋Š” ๊ฒƒ๋“ค:

  • AddToRoot()๋กœ ์ง์ ‘ ๋“ฑ๋กํ•œ UObject
  • ์›”๋“œ์— ์†Œ์†๋œ AActor
  • ๊ธ€๋กœ๋ฒŒ UEngine, UGameInstance ๋“ฑ ์—”์ง„ ์‹ฑ๊ธ€ํ„ด
  • UPROPERTY๋กœ ์ฐธ์กฐ๋œ ์ฒด์ธ ์ „์ฒด
1
2
3
4
5
6
7
8
9
10
11
UCLASS()
class AMyActor : public AActor {
    GENERATED_BODY()

    // โœ… UPROPERTY โ€” GC๊ฐ€ ์ถ”์ 
    UPROPERTY()
    UMyComponent* SafeComp;

    // โŒ raw ํฌ์ธํ„ฐ โ€” GC๊ฐ€ ๋ชจ๋ฆ„ โ†’ ๋Œ•๊ธ€๋ง ํฌ์ธํ„ฐ!
    UMyComponent* DangerComp;
};

USTRUCT ์•ˆ์—์„œ UObject ์ฐธ์กฐํ•˜๊ธฐ:

1
2
3
4
5
6
7
8
USTRUCT(BlueprintType)
struct FWeaponData {
    GENERATED_BODY()
    UPROPERTY()
    UStaticMesh* WeaponMesh;  // USTRUCT ์ž์ฒด๋Š” GC ๋Œ€์ƒ ์•„๋‹ˆ์ง€๋งŒ
    UPROPERTY()                // ์•ˆ์˜ UPROPERTY๋Š” GC๊ฐ€ ์ถ”์ 
    float Damage;
};

Q6. struct์˜ ํฌ๊ธฐ์™€ ๋ฉ”๋ชจ๋ฆฌ ์ •๋ ฌ (padding & alignment)

์ปดํŒŒ์ผ๋Ÿฌ๋Š” CPU์˜ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ํšจ์œจ์„ ์œ„ํ•ด ํŒจ๋”ฉ ๋ฐ”์ดํŠธ๋ฅผ ์‚ฝ์ž…ํ•ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ๋น„ํšจ์œจ์  ๋ฐฐ์น˜ โ€” 12๋ฐ”์ดํŠธ
struct Bad {
    char  a;   // 1 + 3 padding
    int   b;   // 4
    char  c;   // 1 + 3 padding
};

// ํšจ์œจ์  ๋ฐฐ์น˜ โ€” 8๋ฐ”์ดํŠธ
struct Good {
    int   b;   // 4
    char  a;   // 1
    char  c;   // 1 + 2 padding
};

// #pragma pack๋กœ ์ •๋ ฌ ์ œ์–ด
#pragma pack(push, 1)
struct Packed {
    char  a;   // 1
    int   b;   // 4
    char  c;   // 1
};  // sizeof = 6
#pragma pack(pop)

Q7. ๋นˆ struct/class์˜ ํฌ๊ธฐ๋Š”?

C++ ํ‘œ์ค€์—์„œ ๋ชจ๋“  ๊ฐ์ฒด๋Š” ๊ณ ์œ ํ•œ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ ธ์•ผ ํ•˜๋ฏ€๋กœ, ๋นˆ struct/class๋„ ์ตœ์†Œ 1๋ฐ”์ดํŠธ์ž…๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
struct Empty {};
sizeof(Empty);       // 1

// Empty Base Optimization (EBO)
struct Derived : Empty {
    int x;
};
sizeof(Derived);     // 4

C++20์˜ [[no_unique_address]] ์†์„ฑ์œผ๋กœ ๋ฉค๋ฒ„์—๋„ EBO์™€ ๊ฐ™์€ ์ตœ์ ํ™” ๊ฐ€๋Šฅ.

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

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

Powered by Jekyll with Chirpy theme

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