C++ Struct와 Class의 차이 (C++ 관점과 Unreal 관점 비교)

mingu Lee·2025년 11월 16일

CS

목록 보기
1/21

C++에는 여러 데이터 집합을 담을 수 있는 방법이 여러 가지 있는데, 대표적으로 구조체(Struct)와 클래스가 있다.

C++에서 structclass는 생각보다 많은 부분이 비슷하지만, 몇 가지 다른 부분이 존재한다.

1. 공통점


C++ 표준에서 structclass는 문법적으로 거의 동일한 사용자 정의 타입이다.

대표적인 공통점은 다음과 같다.

  • 멤버 구성
    멤버 변수, 멤버 함수, 정적 멤버, 생성자, 소멸자 등
    -> structclass 둘 다 전부 사용 가능
  • 상속
    둘 다 다른 struct/class를 상속할 수 있고, 다중 상속도 가능
  • 접근 지정자 사용 가능
    둘 다 public, protected, private을 자유롭게 사용할 수 있음
  • 멤버 초기화 / 생성자
    둘 다 멤버 이니셜라이저, 이동/복사 생성자, 이동/복사 대입 연산자 등 사용 가능

2. 차이점


2-1. 기본 접근 지정자 (Default Access Specifier)


가장 큰 문법적 차이는 아무것도 안 썼을 때 기본 접근 수준이 무엇인가이다.

class

class A
{
	int x; // 기본: private
    
public:
	void Func();
}
  • Class는 아무것도 안 쓸 경우 기본이 private
  • 멤버 변수/함수 모두 private으로 취급함

struct

struct B
{
    int x;        // 기본: public
    void func();  // 기본: public
};
  • Struct는 아무것도 안 쓸 경우 기본이 public

예시 비교

class PlayerClass
{
    int hp;  // private

public:
    void SetHP(int value) { hp = value; }
};

struct PlayerStruct {
    int hp;  // public
};

int main()
{
    PlayerClass pc;
    // pc.hp = 100;  // 컴파일 에러 (private)

    pc.SetHP(100); // public 함수로만 접근

    PlayerStruct ps;
    ps.hp = 100;   // 바로 접근 (public)
}
  • 실제 설계에서 데이터를 외부에 보이지 않게 하고 싶으면 class를 사용
  • 그냥 데이터 묶음이면 struct로 사용

2-2. 상속 시 기본 접근 지정자 (Default Inheritance)


class는 기본이 private 상속

class Base
{
public:
    void Foo() {}
};

class Derived : Base // 기본: private 상속
// (class Derived : private Base; 와 동일)
{
};

int main()
{
    Derived d;
    // d.Foo();   // private 상속이라 Base의 public 멤버가 외부에 안 보임
}

struct는 기본이 public 상속

struct Base
{
public:
    void Foo() {}
};

struct Derived : Base // 기본: public 상속
// (struct Derived : public Base; 와 동일)
{
};

int main()
{
    Derived d;
    d.Foo();  // public 상속이라 그대로 사용 가능
}

즉,

  • class -> private 상속이 기본
  • struct -> public 상속이 기본

2-3. 실제 설계 관점에서의 차이 (관례 / 스타일)


위의 두 가지는 문법적인 차이이고, 그 외의 차이는 대부분 사람들이 이러한 경우에 쓰자고 하는 관례에 가깝다.

struct를 주로 쓰는 경우

  • 데이터 중심 타입
  • 멤버가 거의 다 public이고, 특별한 캡슐화가 필요 없는 경우
struct Vector3
{
    float x;
    float y;
    float z;

    Vector3(float x, float y, float z)
        : x(x), y(y), z(z) {}

    float LengthSquared() const
    {
        return x*x + y*y + z*z;
    }
};

struct Stats
{
    int hp;
    int mp;
    int stamina;
};

사용할 땐 직접 접근해서 사용한다.

Vector3 pos(1.0f, 2.0f, 3.0f);
Stats s{ 100, 50, 30 };

s.hp -= 10;     // 바로 접근해도 큰 문제 없음
float len2 = pos.LengthSquared();

class를 주로 쓰는 경우

  • 내부 상태를 숨기고, 멤버 함수(인터페이스)를 통해서만 조작하고 싶을 때
  • 복잡한 행동/로직이 필요할 때
class Player
{
public:
    Player()
        : hp(100), maxHp(100) {}

    void TakeDamage(int damage)
    {
        hp -= damage;
        if (hp < 0) hp = 0;
    }

    void Heal(int amount)
    {
        hp += amount;
        if (hp > maxHp) hp = maxHp;
    }

    int GetHP() const { return hp; }

private:
    int hp;
    int maxHp;
};

사용할 땐 멤버 함수를 통해 접근한다.

int main()
{
    Player p;
    // p.hp = 9999;  // 직접 조작 불가
    p.TakeDamage(30);
    p.Heal(20);
}

2-4. Unreal에서의 struct(USTRUCT)와 class(UCLASS)의 차이


C++에서는 struct와 class의 차이가 대부분 접근 지정자(default access) 정도로 단순하지만, Unreal Engine에서는 USTRUCT와 UCLASS 사이에 큰 기능적 차이가 존재한다.

Unreal의 리플렉션 시스템, GC, UObject 생명 주기, Blueprint, 네트워크 등
엔진 전반에 걸쳐 USTRUCT와 UCLASS는 완전히 다르게 동작한다.

대표적인 차이를 정리하면 다음과 같다.

메모리 모델과 수명 주기 (Value Type vs Reference Type)


USTRUCT

USTRUCT(BlueprintType)
struct FPlayerStats
{
    GENERATED_BODY()
    
    UPROPERTY(EditAnywhere)
    int HP;
};
  • 값 타입(Value Type)
    → 스택, 멤버 변수 등 그 자리(value)에 저장됨
  • new/delete로 동적 생성하지 않음
  • 소멸 시점은 C++ 변수가 종료될 때 자동으로 결정
  • GC(가비지 컬렉션)에 의해 추적되지 않음

UCLASS

UCLASS()
class UMyObject : public UObject
{
    GENERATED_BODY()
};
  • 참조 타입(Reference Type)
    → NewObject, SpawnActor로 힙에 생성됨
  • 반드시 포인터로 접근해야 함
  • GC에 의해 생명 주기 관리
  • 순환 참조 문제를 GC가 관리

즉,
데이터 중심 = USTRUCT
객체/시스템 = UCLASS

상속 및 다형성 여부


USTRUCT

  • 상속은 가능하지만 virtual 불가
  • vtable 생성 안 됨
  • 다형성(polymorphism) 불가능
  • UObject 시스템에 포함되지 않음

UCLASS

  • 자유로운 상속 가능 (UObject 계열)
  • virtual / override / 다형성 완전 지원
  • UInterface 사용 가능
  • 엔진 전체 시스템과 연동됨 (Delegates, Components 등)

즉, 로직/행동이 필요한 경우 반드시 UCLASS여야 한다.

리플렉션 및 Blueprint 지원


USTRUCT

  • 리플렉션 지원은 “멤버 변수 수준”
  • BlueprintType으로 BP 변수로 사용 가능
  • Blueprint에서 생성은 불가능 (Value Type)
  • UFUNCTION 지원 X
  • Replication 메타데이터 제한적

UCLASS

  • 모든 리플렉션 지원
  • Blueprint Class 생성 가능
  • EventGraph, 함수 Override 등 가능
  • Replicated, Server/Client RPC, NetMulticast 등 네트워크 기능 지원
  • Component/Subobject 생성 가능

즉, BP/네트워크/함수 기반 확장이 필요하면 무조건 UCLASS.

가비지 컬렉션(GC) 처리


USTRUCT

  • GC 대상이 아님
  • 단, 내부 멤버의 UObject*가 UPROPERTY()로 되어 있으면 GC가 해당 객체만 추적
USTRUCT()
struct FItem
{
    GENERATED_BODY()

    UPROPERTY()
    UTexture2D* Icon; // GC가 추적
};

UCLASS

  • GC가 전체 객체 생명 주기를 관리
  • Reference Tracking을 통해 메모리 누수 방지
  • 모든 UPROPERTY() UObject*를 자동 추적

실제 사용 패턴 (언리얼이 권장하는 관례)


Struct를 주로 쓰는 경우

  • 단순 데이터 묶음
  • Value Type (FVector, FTransform 등)
  • 스탯, 인벤토리 아이템, 설정값 등
  • 복사/이동이 잦은 데이터

Class를 주로 쓰는 경우

  • Actor/Component/Widget
  • 시스템 클래스(GameMode, GameState 등)
  • Blueprint 확장 가능해야 하는 경우
  • 동적으로 생성되는 객체

3. 결론


C++ 관점

struct = public인 class

구조적으로 거의 동일하며, 대부분 설계/관례로 구분한다.

Unreal Engine 관점

USTRUCT = 값 타입(데이터), GC 없음, 가벼움
UCLASS = 객체 타입(로직), GC 관리, Blueprint/Network/Reflection 전체 지원

profile
Github: https://github.com/dlalsrn

0개의 댓글