C++, CS

[C++] class와 struct의 차이 정리

dhlee-dev 2026. 4. 17. 20:27

오늘은 C++의 classstruct의 차이를 정리해보려고 한다.

structclass가 완전히 다른 개념처럼 느껴질 수 있는데,
실제로는 사용할 수 있는 기능이 거의 동일하다.

다만 C++에서는 둘의 기본 접근 지정자기본 상속 접근 지정자가 다르고,
관례적으로도 서로 다른 의도를 담아서 사용하는 경우가 많다.

추가로 이번 글에서는 struct / class의 멤버가 메모리에 어떻게 배치되는지도 함께 정리해보았다.


classstruct의 차이

C++에서 structclass는 기능상 거의 동일하다.

예를 들어 둘 다 다음과 같은 기능을 가질 수 있다.

  • 멤버 변수 / 멤버 함수
  • 생성자 / 소멸자
  • 상속
  • 템플릿
  • 접근 지정자 사용

문법적으로 사용할 수 있는 기능은 거의 같고, 차이는 다음 두 가지다.

  • 기본 멤버 접근 지정자
  • 기본 상속 접근 지정자

접근 지정자의 기본값은 다음과 같다.

  • struct

    • 기본 멤버 접근 지정자 : public
    • 기본 상속 접근 지정자 : public
  • class

    • 기본 멤버 접근 지정자 : private
    • 기본 상속 접근 지정자 : private

즉, 별도로 접근 지정자를 작성하지 않는다면 접근 지정자의 기본값은 다르고,
기능은 거의 동일하다.


접근 지정자란?

접근 지정자(access specifier) 란 멤버에 대한 접근 권한을 제어하는 키워드이다.

대표적으로 다음 세 가지가 있다.

  • public : 어디서든 멤버에 접근할 수 있다.
  • protected : 클래스 내부와 파생 클래스에서 접근할 수 있다.
  • private : 해당 클래스 내부에서만 접근할 수 있다.

접근 지정자는 캡슐화를 구현할 때 핵심적으로 사용된다.

예외적으로 C++에서는 friend 키워드로 지정된 함수나 클래스가
해당 클래스의 private, protected 멤버에 접근할 수 있다.


기능상 거의 동일한데 왜 구분할까?

기능상 거의 동일하다면 굳이 왜 structclass를 나눠서 사용할까?

이유는 코드를 읽는 사람에게 의도를 더 명확하게 전달하기 위해서다.

보통 관례상 다음과 같이 사용한다.

  • struct : 데이터의 묶음
  • class : 캡슐화가 필요한 객체

예를 들어 Position처럼 단순히 좌표 값을 묶어 표현하는 타입은 'struct'로 나타내기 좋다.
반면 Player, Dog처럼 상태와 동작을 함께 가지며 캡슐화가 필요한 객체는 'class'로 표현하는 것이 더 자연스럽다.

물론 struct 안에 private 멤버를 두고 게터 / 세터를 만들어서 사실상 class처럼 설계하는 것도 가능하다.
하지만 이렇게 하면 기능적으로는 문제없어도, 다른 사람이 코드를 읽을 때 의도를 다르게 해석할 수 있다.

특히 협업에서는 “이 타입이 단순 데이터 묶음인지, 캡슐화된 객체인지”
이런 의도가 빠르게 읽히는 것이 중요하므로 관례를 지키는 편이 좋다고 생각한다.


classstruct 혼용 예시

아래는 structclass가 기능적으로 거의 동일하다는 점과,
기본 상속 접근 지정자의 차이를 확인할 수 있는 예시 코드이다.

#include <iostream>

using namespace std;

struct A {
    int x;
};

class B : A {
public:
    int y;
};

class C {
public:
    int x;
};

struct D : C {
    int y;
};

int main() {
    B test1{};
    D test2{};

    // test1.x;  // class는 기본 상속이 private라 외부에서 접근 불가, 컴파일 에러!!
    test1.y;

    test2.x;
    test2.y;
}

'struct' 와 'class' 는 기능상 거의 동일하므로 위 코드처럼 서로를 상속받을 수 있다.

위 코드에서 중요한 점은 다음과 같다.

  • class B : Aprivate 상속
  • struct D : Cpublic 상속

앞서 설명했듯이 BA를 상속받을때 기본 상속 접근 지정자가 private이므로
Apublic 멤버 x를 외부에서 그대로 접근할 수 없다.

반면 DC를 상속받을때 기본 상속 접근 지정자가 'public'이므로
Cpublic 멤버 x에 외부에서 그대로 접근할 수 있다.

실제로 이런 식으로 혼용해서 사용할 일은 많지 않지만,
사용하게 된다면 아래처럼 상속 접근 지정자를 명시적으로 적는 것이 더 좋다.

struct D : public C {
    int y;
};

추가 정리 : struct / class의 메모리 배치와 패딩

추가로 structclass의 멤버가 메모리에 어떻게 배치되는지도 알아보려고 한다.

CPU는 보통 데이터를 처리할 때 각 타입의 정렬 조건(alignment) 에 맞게
배치된 데이터를 더 효율적으로 처리한다.

일반적인 경우에 다음과 같이 예시를 들 수 있다.

  • int는 4바이트 정렬을 요구하는 경우가 많다.
  • double은 8바이트 정렬을 요구하는 경우가 많다.

즉, int는 보통 4의 배수 주소(0, 4, 8, ...),
double은 보통 8의 배수 주소(0, 8, 16, ...)
이렇게 놓일 때 CPU가 더 효율적으로 접근할 수 있다.

정렬 조건을 만족하지 않으면 CPU가 데이터를 읽을 때 추가 비용이 발생할 수 있다.

따라서 컴파일러는 CPU가 구조체 / 클래스의 데이터를 효율적으로 읽을 수 있도록 멤버를 배치하는데,
이때 정렬 조건을 만족하기 위해 일부러 빈 공간(padding) 을 추가하기도 한다.


패딩이란?

컴파일러는 각 멤버가 자신의 정렬 조건을 만족하도록 멤버 사이에 빈 공간(padding) 을 추가할 수 있다.

구조체나 클래스의 실제 크기를 확인해보면 단순히 멤버 크기를 더한 값과 다를 수 있다.
이때, 구조체나 클래스의 크기는 보통 가장 큰 정렬 조건의 배수가 되도록 맞춰진다.


예시 1

struct S {
    char a;
    char b;
    int c;
    char d;
};

위 구조체를 보면 단순히 생각했을 때

  • char 1바이트
  • char 1바이트
  • int 4바이트
  • char 1바이트

이므로 총 7바이트처럼 보일 수 있다.

하지만 실제로는 보통 12바이트가 된다.

대략적인 배치는 다음과 같다.

  • a : 1바이트
  • b : 1바이트
  • 패딩 : 2바이트
  • c : 4바이트
  • d : 1바이트
  • 패딩 : 3바이트

가장 큰 정렬조건인 int의 정렬 조건을 맞추기 위해 중간에 패딩이 들어가고,
전체 구조체 크기도 정렬 단위에 맞게 커지는 것이다.


예시 2

만약 위 구조체에서 int cdouble c로 바꾸면 어떻게 될까?

struct S {
    char a;
    char b;
    double c;
    char d;
};

이 경우 구조체 크기는 보통 24바이트가 된다.

대략적인 배치는 다음과 같다.

  • a : 1바이트
  • b : 1바이트
  • 패딩 : 6바이트
  • c : 8바이트
  • d : 1바이트
  • 패딩 : 7바이트

double이 보통 8바이트 정렬을 요구하기 때문에 이에 맞춰 패딩이 더 많이 들어가게 된다.


패딩을 줄이는 방법

패딩은 멤버 순서를 조정해서 줄일 수 있다.

예를 들어 정렬 조건이 큰 멤버를 앞쪽에 두면 불필요한 패딩을 줄일 수 있는 경우가 많다.

위 예시에서 멤버 순서를 바꿔서 아래와 같이 작성하면

struct S {
    double c;
    char a;
    char b;
    char d;
};

구조체 크기가 더 줄어드는 것을 확인할 수 있다.
이 경우 보통 16바이트가 된다.

그러므로, 성능이나 메모리 사용량이 중요한 상황에서는 멤버 순서까지 고려하는 것이 좋다.


패딩을 강제로 조정하는 방법

패딩은 #pragma pack 지시어를 사용해서 조정할 수 있다.

#pragma pack(push, 1)
struct S {
    char a;
    int b;
};
#pragma pack(pop)

위 예시에서는 정렬 단위를 1바이트로 맞추므로 패딩이 줄어들거나 제거될 수 있다.
이 경우 보통 char 1바이트 + int 4바이트로 계산되어 총 크기가 5바이트가 된다.

다만 #pragma pack은 컴파일러 의존적인 기능이며, 무조건 사용하는 것이 좋은 것은 아니다.
정렬을 강제로 깨면 메모리는 줄어들 수 있지만, 오히려 CPU 접근 성능이 나빠질 수도 있고 이식성에도 영향을 줄 수 있다.

그래서 특별한 이유가 없다면 우선은 멤버 순서를 조정하는 방식을 먼저 고려하는 편이 더 자연스럽다.


정리

C++에서 structclass는 기능상 거의 동일하다.

하지만 기본 접근 지정자, 기본 상속 접근 지정자에서 차이가 있다.

  • struct는 둘 다 'public'
  • class는 둘 다 'private'

또한 실제 코드에서는 단순히 문법 차이 때문이 아니라
의도를 드러내기 위한 관례로 구분해서 사용하는 경우가 많다.

  • 단순 데이터 묶음 → struct
  • 캡슐화가 필요한 객체 → class

추가로 struct / class의 멤버는 정렬 조건에 따라 메모리에 배치되고,
이 과정에서 패딩이 들어갈 수 있다.

따라서 struct / class를 설계할 때는 멤버 순서를 고려하는 것이 좋다.