본문 바로가기

class와 struct의 차이

@iamrain2025. 11. 13. 10:58

1. 이론

C++에서 structclass는 사용자가 새로운 데이터 타입을 정의할 수 있게 해주는 강력한 도구입니다. 두 키워드는 기술적으로 거의 동일한 기능을 제공하지만, 주된 차이점은 기본 접근 제어자와 상속 접근 지정자에 있습니다.

1.1. 핵심 차이점: 기본 접근성

가장 명확하고 본질적인 차이점은 멤버의 기본 접근 수준입니다.

  • struct: 멤버의 기본 접근 지정자는 public입니다. 즉, struct를 정의할 때 접근 지정자를 명시하지 않으면 모든 멤버 변수와 멤버 함수는 외부에서 자유롭게 접근할 수 있습니다.
  • class: 멤버의 기본 접근 지정자는 private입니다. class에서 접근 지정자를 생략하면 모든 멤버는 기본적으로 해당 클래스의 멤버 함수를 통해서만 접근할 수 있습니다.
struct MyStruct {
    int data; // 기본적으로 public
};

class MyClass {
    int data; // 기본적으로 private
};

int main() {
    MyStruct s;
    s.data = 10; // 가능

    MyClass c;
    // c.data = 10; // 컴파일 에러: 'data' is private
    return 0;
}

1.2. 상속에서의 기본 접근 지정자

상속을 받을 때도 기본 접근 지정자에 차이가 있습니다.

  • struct: 기본적으로 public 상속을 합니다. 이는 부모 structpublic 멤버들이 자식 struct에서도 public으로 유지됨을 의미합니다.
  • class: 기본적으로 private 상속을 합니다. 부모 classpublicprotected 멤버들이 자식 class 내에서는 private 멤버가 됩니다.
struct ParentStruct {};
struct ChildStruct : ParentStruct {}; // public 상속

class ParentClass {};
class ChildClass : ParentClass {}; // private 상속

1.3. 사용법

기술적인 차이점 외에, C++ 커뮤니티에서는 두 키워드를 다음과 같은 차이에 따라 사용합니다.

  • struct: POD(Plain Old Data) 타입을 정의할 때 주로 사용됩니다. POD는 C 스타일의 구조체처럼 순수하게 데이터만을 담는 역할을 하며, 복잡한 로직이나 데이터 은닉의 필요성이 적은 경우에 적합합니다. 예를 들어, 3D 좌표(Vector3D), 색상 값(Color), 설정 값 모음(Config) 등을 정의할 때 유용합니다. 모든 데이터가 공개되어 있고, 외부에서 데이터를 직접 조작하는 것이 자연스러운 경우입니다.
  • class: 객체 지향 프로그래밍(OOP)의 핵심 요소를 구현할 때 사용됩니다. class는 데이터와 그 데이터를 조작하는 메서드(멤버 함수)를 캡슐화하고, 정보 은닉(Information Hiding), 상속(Inheritance), 다형성(Polymorphism)을 통해 복잡한 시스템을 모델링합니다. class는 내부 상태(데이터)를 private으로 보호하고, public 인터페이스를 통해 상태를 조작하도록 강제함으로써 불변성(invariants)을 유지하고 객체의 무결성을 보장합니다. 예를 들어, Player, Monster, Inventory와 같이 자체적인 로직과 상태를 가지는 객체를 설계할 때 적합합니다.

1.4. 내부 구조 및 메모리 레이아웃

structclass는 컴파일된 후 메모리에 배치되는 방식에서 차이가 없습니다. 두 키워드 모두 멤버 변수들이 선언된 순서대로 메모리에 연속적으로 배치됩니다(컴파일러의 패딩 최적화는 예외). 가상 함수가 포함되면, 객체의 메모리 맨 앞부분에 가상 함수 테이블 포인터(vptr)가 추가되어 런타임 다형성을 지원하게 됩니다. 이 또한 structclass 모두에게 동일하게 적용됩니다.

결론적으로, C++ 표준에 따르면 struct는 모든 멤버가 기본적으로 publicclass이고, class는 모든 멤버가 기본적으로 privatestruct입니다. 컴파일러는 이 두 키워드를 거의 동일하게 취급하며, 최종적인 기계어 코드에서는 차이가 없습니다.

1.5. 동작 계층 구조

structclass의 차이는 주로 컴파일 타임에 의미를 가집니다.

  1. 사용자 영역 (Source Code): 개발자는 struct를 데이터 묶음으로, class를 캡슐화된 객체로 의도하여 코드를 작성합니다. 이는 코드의 가독성과 유지보수성에 영향을 줍니다.
  2. 컴파일러 영역 (Compile Time): 컴파일러는 structclass의 기본 접근 지정자 규칙을 적용하여 접근 권한을 검사합니다. 허용되지 않은 접근(예: private 멤버에 외부에서 접근)이 있으면 컴파일 에러를 발생시킵니다. 이 단계에서 두 키워드의 구문적 차이가 처리됩니다.
  3. 실행 파일 및 메모리 영역 (Runtime): 컴파일이 완료된 후 생성된 실행 파일에는 structclass의 구분이 없습니다. 런타임 시점에서 객체가 메모리에 생성될 때, 두 타입은 동일한 방식으로 메모리 공간을 차지합니다. 커널 수준에서는 이들을 구분하지 않고 단순히 메모리 블록으로 취급합니다.

1.6. 비교 요약: 언제 무엇을 사용해야 하는가?

특징 struct class
기본 접근 지정자 public private
기본 상속 지정자 public private
주요 사용 목적 데이터 묶음 (POD), 수동적 데이터 컨테이너 캡슐화, 정보 은닉, 불변성 유지가 필요한 능동적 객체
장점 데이터 구조를 명확하고 간결하게 표현 객체의 상태를 안전하게 보호하고, 복잡한 로직을 구조화하기 용이
단점 데이터 캡슐화가 어려워 객체 상태가 깨지기 쉬움 간단한 데이터 구조에 사용하기에는 코드가 장황해질 수 있음
사용 예시 Vector3, Color, PlayerInfo PlayerController, InventoryManager, DatabaseConnection

결론: 데이터의 집합을 표현하고 싶고, 그 데이터에 대한 특별한 제약 조건이나 로직이 없다면 struct를 사용하세요. 반면, 데이터와 함께 그 데이터를 처리하는 행위를 묶고, 객체의 상태를 보호하며 특정 인터페이스를 통해서만 상호작용하도록 강제하고 싶다면 class를 사용하는 것이 좋습니다.

2. 요약

 structclass의 가장 큰 기술적 차이는 기본 접근 지정자에 있습니다.

 struct의 멤버는 기본적으로 public이고, class의 멤버는 private입니다. 상속 시에도 structpublic 상속이, classprivate 상속이 기본값입니다.

 하지만 더 중요한 것은 사용의 차이입니다. struct는 주로 C 스타일 구조체처럼 순수한 데이터 묶음, 즉 POD(Plain Old Data) 타입을 정의하는 데 사용됩니다. 데이터 은닉보다는 데이터 자체를 표현하는 것이 중요할 때 적합합니다. 반면, class는 데이터와 메서드를 캡슐화하여 정보 은닉, 상속, 다형성 등 객체 지향 프로그래밍의 개념을 구현하는 데 사용됩니다. 객체의 상태와 행위를 함께 관리하고, 내부 데이터의 무결성을 보장해야 할 때 class를 사용하는 것이 일반적입니다.

 컴파일된 후 메모리 구조상으로는 둘 사이에 차이가 없으며, 가상 함수 사용 여부에 따라 가상 함수 테이블 포인터가 추가되는 것 또한 동일합니다. 결국, 어떤 것을 사용할지는 정의하려는 타입이 단순한 데이터 덩어리인지, 아니면 독립적인 상태와 행위를 가지는 객체인지에 따라 결정하는 것이 좋습니다.

3. 실습 코드

 다음은 게임 캐릭터의 위치와 상태를 관리하는 예시 코드입니다. struct는 3차원 좌표처럼 순수한 데이터를 위해, class는 체력과 같은 내부 상태를 보호하고 관련 로직을 캡슐화하기 위해 사용되었습니다.

#include <iostream>
#include <string>

struct Vector3D {
    float x, y, z;
};

class Player {
private:
    std::string name;
    int hp;
    int maxHp;
    Vector3D position;

public:
    Player(const std::string& name, int initialHp, Vector3D startPos)
        : name(name), maxHp(initialHp), hp(initialHp), position(startPos) {
        std::cout << name << " 플레이어가 생성되었습니다. (HP: " << hp << "/" << maxHp << ")" << std::endl;
    }

    void takeDamage(int damage) {
        if (damage < 0) return;

        hp -= damage;
        if (hp < 0) {
            hp = 0;
        }
        std::cout << name << "이(가) " << damage << "의 데미지를 입었습니다. (현재 HP: " << hp << ")" << std::endl;

        if (isDead()) {
            std::cout << name << "이(가) 쓰러졌습니다." << std::endl;
        }
    }

    void heal(int amount) {
        if (amount < 0) return;

        hp += amount;
        if (hp > maxHp) {
            hp = maxHp;
        }
        std::cout << name << "이(가) " << amount << "만큼 체력을 회복했습니다. (현재 HP: " << hp << ")" << std::endl;
    }

    void move(Vector3D delta) {
        position.x += delta.x;
        position.y += delta.y;
        position.z += delta.z;
    }

    bool isDead() const {
        return hp <= 0;
    }

    void printStatus() const {
        std::cout << "--- " << name << " 상태 ---" << std::endl;
        std::cout << "HP: " << hp << "/" << maxHp << std::endl;
        std::cout << "위치: (" << position.x << ", " << position.y << ", " << position.z << ")" << std::endl;
        std::cout << "--------------------" << std::endl;
    }
};

int main() {
    Vector3D startPosition = {0.0f, 0.0f, 0.0f};

    Player myPlayer("전사", 100, startPosition);

    myPlayer.printStatus();

    myPlayer.takeDamage(30);
    myPlayer.printStatus();

    myPlayer.heal(20);
    myPlayer.printStatus();

    Vector3D moveVec = {10.0f, 5.0f, 0.0f};
    myPlayer.move(moveVec);
    myPlayer.printStatus();

    myPlayer.takeDamage(150);
    myPlayer.printStatus();

    myPlayer.heal(50);
    myPlayer.printStatus();

    return 0;
}
iamrain
@iamrain :: Annals of Unreal

iamrain 님의 블로그 입니다.

공감하셨다면 ❤️ 구독도 환영합니다! 🤗

목차