Namespaces
Variants

Non-static data members

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

비정적 데이터 멤버는 클래스의 멤버 명세 에서 선언됩니다.

class S
{
    int n;              // 비정적 데이터 멤버
    int& r;             // 참조 타입의 비정적 데이터 멤버
    int a[2] = {1, 2};  // 기본 멤버 초기화자를 가진 비정적 데이터 멤버 (C++11)
    std::string s, *ps; // 두 개의 비정적 데이터 멤버
    struct NestedS
    {
        std::string s;
    } d5;               // 중첩 타입의 비정적 데이터 멤버
    char bit : 2;       // 2비트 비트 필드
};

다음을 제외한 모든 단순 선언 이 허용됩니다:

  • thread_local 저장 클래스 지정자는 허용되지 않습니다 (단, static 데이터 멤버에 대해서는 허용됨);
(since C++11)
  • 불완전 타입 , 추상 클래스 타입 및 이들의 배열은 허용되지 않습니다: 특히, 클래스 C C 타입의 비정적 데이터 멤버를 가질 수 없지만, C& (C에 대한 참조) 또는 C* ( C 에 대한 포인터) 타입의 비정적 데이터 멤버는 가질 수 있습니다;
  • 사용자 선언 생성자가 하나 이상 존재하는 경우, 비정적 데이터 멤버는 클래스 이름과 동일한 이름을 가질 수 없습니다;
(C++11부터)

또한, bit-field 선언이 허용됩니다.

목차

레이아웃

어떤 클래스 C 의 객체가 생성될 때, 비-참조 타입의 각 비정적 데이터 멤버는 C 의 객체 표현의 일부 영역에 할당됩니다. 참조 멤버들이 저장 공간을 차지하는지는 구현에 따라 정의되지만, 그들의 storage duration 은 자신이 멤버로 속한 객체의 저장 지속 기간과 동일합니다.

non- union 클래스 타입의 경우, non-zero-sized (C++20부터) 멤버들은 access specifier로 구분되지 않은 (C++11 이전) 동일한 member access를 가진 (C++11부터) 항상 나중에 선언된 멤버들이 클래스 객체 내에서 더 높은 주소를 가지도록 할당됩니다. access specifier로 구분된 멤버들 (C++11 이전) 다른 access control을 가진 멤버들 (C++11부터) 은 지정되지 않은 순서로 할당됩니다 (컴파일러가 이들을 함께 그룹화할 수 있습니다).

(C++23 이전)

non- union 클래스 타입의 경우, non-zero-sized 멤버들은 항상 나중에 선언된 멤버들이 클래스 객체 내에서 더 높은 주소를 가지도록 할당됩니다. 멤버의 access control이 여전히 standard-layout 속성에 영향을 미친다는 점에 유의하십시오 (아래 참조).

(C++23부터)

정렬 요구 사항으로 인해 클래스 멤버 사이 또는 마지막 멤버 뒤에 패딩이 필요할 수 있습니다.

표준 레이아웃

클래스는 다음 속성을 가지는 표준 레이아웃(standard-layout) 클래스로 간주되며, 이는 해당 클래스가 POD 클래스 인 경우에만 해당됩니다.

(until C++11)

모든 비정적 데이터 멤버가 동일한 접근 제어를 가지고 특정 다른 조건들을 만족하는 클래스는 표준 레이아웃 클래스(standard-layout class) 로 알려져 있습니다 (요구 사항 목록은 표준 레이아웃 클래스 를 참조하십시오).

(since C++11)

두 표준 레이아웃 비-공용체 클래스 타입의 공통 초기 시퀀스 는 각 클래스의 첫 번째 비정적 데이터 멤버와 비트 필드부터 시작하여, 선언 순서대로 배열된 비정적 데이터 멤버와 비트 필드의 가장 긴 시퀀스로서 다음 조건을 만족합니다:

  • 만약 __has_cpp_attribute ( no_unique_address ) 0 이 아니면, 두 엔티티 모두 [[ no_unique_address ]] 속성으로 선언되지 않습니다,
(C++20부터)
  • 해당 엔티티들이 레이아웃 호환 타입을 가지며,
  • 해당 엔티티들이 동일한 alignment requirements 을 가지며,
  • 두 엔티티 모두 동일한 너비의 비트 필드이거나, 둘 다 비트 필드가 아닌 경우.
struct A { int a; char b; };
struct B { const int b1; volatile char b2; }; 
// A와 B의 공통 초기 시퀀스는 A.a, A.b와 B.b1, B.b2입니다
struct C { int c; unsigned : 0; char b; };
// A와 C의 공통 초기 시퀀스는 A.a와 C.c입니다
struct D { int d; char b : 4; };
// A와 D의 공통 초기 시퀀스는 A.a와 D.d입니다
struct E { unsigned int e; char b; };
// A와 E의 공통 초기 시퀀스는 비어 있습니다

두 표준 레이아웃 비-공용체 클래스 타입은 cv 한정자를 무시했을 때 동일한 타입이거나, 레이아웃 호환 열거형 (즉, 동일한 기반 타입을 가진 열거형)이거나, 또는 그들의 공통 초기 시퀀스 가 모든 비정적 데이터 멤버와 비트 필드를 포함하는 경우 레이아웃 호환 이라고 합니다 (위 예제에서 A B 는 레이아웃 호환입니다).

두 표준 레이아웃 공용체는 동일한 수의 비정적 데이터 멤버를 가지고 있고, 해당 비정적 데이터 멤버들(어떤 순서로든)이 레이아웃 호환 가능한 타입을 가질 때 레이아웃 호환 가능 하다고 합니다.

표준 레이아웃 타입은 다음과 같은 특별한 속성을 가집니다:

  • 표준 레이아웃(standard-layout) 공용체에서 비공용체 클래스 타입 T1 의 활성 멤버가 있을 때, 다른 비공용체 클래스 타입 T2 의 공용체 멤버에 속한 비정적 데이터 멤버 m 을 읽는 것이 허용됩니다. 단, m T1 T2 의 공통 초기 시퀀스(common initial sequence)에 속하는 경우에 한합니다 (단, volatile 멤버를 비-volatile glvalue를 통해 읽는 것은 정의되지 않은 동작입니다).
  • 표준 레이아웃 클래스 타입의 객체에 대한 포인터는 reinterpret_cast 를 사용하여 첫 번째 비정적 비-비트필드 데이터 멤버(비정적 데이터 멤버가 있는 경우) 또는 기반 클래스 서브객체(기반 클래스가 있는 경우)에 대한 포인터로 변환할 수 있으며, 그 역도 성립합니다. 즉, 표준 레이아웃 타입의 첫 번째 데이터 멤버 앞에는 패딩(padding)이 허용되지 않습니다. 단, 엄격한 별칭 지정(strict aliasing) 규칙은 이러한 변환 결과에 여전히 적용됩니다.
  • 매크로 offsetof 를 사용하여 표준 레이아웃 클래스의 시작점부터 어떤 멤버의 오프셋(offset)을 결정할 수 있습니다.

멤버 초기화

비정적 데이터 멤버는 두 가지 방법 중 하나로 초기화될 수 있습니다:

1) 생성자의 멤버 초기화 리스트 에서.
struct S
{
    int n;
    std::string s;
    S() : n(7) {} // n을 직접 초기화하고, s를 기본 초기화함
};
2) 기본 멤버 초기화자(default member initializer) 를 통해, 이는 멤버 선언에 포함된 중괄호 또는 등호 초기화자(initializer) 이며, 생성자의 멤버 초기화 목록에서 해당 멤버가 생략된 경우 사용됩니다.
struct S
{
    int n = 7;
    std::string s{'a', 'b', 'c'};
    S() {} // 기본 멤버 초기화자가 n을 복사 초기화하고, s를 목록 초기화합니다
};

멤버에 기본 멤버 초기화자가 있고 생성자의 멤버 초기화 목록에도 나타나는 경우, 해당 생성자에 대해서는 기본 멤버 초기화자가 무시됩니다.

#include <iostream>
int x = 0;
struct S
{
    int n = ++x;
    S() {}                 // 기본 멤버 초기화자 사용
    S(int arg) : n(arg) {} // 멤버 초기화자 사용
};
int main()
{
    std::cout << x << '\n'; // 0 출력
    S s1;                   // 기본 초기화자가 실행됨
    std::cout << x << '\n'; // 1 출력
    S s2(7);                // 기본 초기화자가 실행되지 않음
    std::cout << x << '\n'; // 1 출력
}

비트 필드(bit-field) 멤버에는 기본 멤버 초기화자를 사용할 수 없습니다.

(C++20 이전)

배열 타입의 멤버는 멤버 초기화자로부터 크기를 추론할 수 없습니다:

struct X
{
    int a[] = {1, 2, 3};  // 오류
    int b[3] = {1, 2, 3}; // OK
};

기본 멤버 초기화자는 해당 클래스의 기본 생성자( default constructor )의 암시적 정의를 유발하거나 해당 생성자의 예외 명세를 유발해서는 안 됩니다:

struct node
{
    node* p = new node; // 오류: 암시적 또는 기본 node::node()의 사용
};

참조 멤버는 기본 멤버 초기화자에서 임시 객체에 바인딩될 수 없습니다 (참고: 멤버 초기화 목록 에도 동일한 규칙이 존재함):

struct A
{
    A() = default;     // OK
    A(int v) : v(v) {} // OK
    const int& v = 42; // OK
};
A a1;    // 오류: 참조에 임시 객체 바인딩이 잘못됨
A a2(1); // OK (v가 생성자에 나타나므로 기본 멤버 초기화자가 무시됨)
         // 그러나 a2.v는 댕글링 참조입니다
(C++11 이후)


만약 참조 멤버가 자신의 기본 멤버 초기화자로부터 초기화되고 (C++20까지) 멤버가 기본 멤버 초기화자를 가지고 있고 (C++20부터) 그것의 잠재적으로 평가되는 부분표현식이 집합체 초기화 이며 그 기본 멤버 초기화자를 사용하게 될 경우, 프로그램은 형식에 맞지 않습니다:

struct A;
extern A a;
struct A
{
    const A& a1{A{a, a}}; // OK
    const A& a2{A{}};     // error
};
A a{a, a};                // OK
(C++17부터)

사용법

비정적 데이터 멤버 또는 비정적 멤버 함수의 이름은 다음 세 가지 상황에서만 나타날 수 있습니다:

1) 클래스 멤버 접근 표현식의 일부로서, 해당 클래스가 이 멤버를 가지고 있거나 이 멤버를 가진 클래스로부터 파생된 경우, 암시적 this - > 멤버 접근 표현식을 포함합니다. 이러한 표현식은 비정적 멤버 이름이 this 가 허용되는 모든 컨텍스트(멤버 함수 본문 내부, 멤버 초기화 목록, 클래스 내 기본 멤버 초기화자)에서 사용될 때 나타납니다.
struct S
{
    int m;
    int n;
    int x = m;            // OK: 기본 초기화자에서 암시적 this-> 허용 (C++11)
    S(int i) : m(i), n(m) // OK: 멤버 초기화 목록에서 암시적 this-> 허용
    {
        this->f();        // 명시적 멤버 접근 표현식
        f();              // 멤버 함수 본문에서 암시적 this-> 허용
    }
    void f();
};
2) 비정적 멤버에 대한 포인터 를 형성하기 위해.
struct S
{
    int m;
    void f();
};
int S::*p = &S::m;       // OK: use of m to make a pointer to member
void (S::*fp)() = &S::f; // OK: use of f to make a pointer to member
3) (데이터 멤버에만 해당, 멤버 함수는 제외) 평가되지 않는 피연산자 내에서 사용될 때.
struct S
{
    int m;
    static const std::size_t sz = sizeof m; // OK: m in unevaluated operand
};
std::size_t j = sizeof(S::m + 42); // OK: even though there is no "this" object for m
참고: 이러한 사용은 일부 컴파일러(예: clang)에서 C++11의 변경 사항으로 취급되는 N2253 에서 CWG issue 613 의 해결을 통해 허용됩니다.

참고 사항

기능 테스트 매크로 표준 기능
__cpp_nsdmi 200809L (C++11) 비정적 데이터 멤버 초기화
__cpp_aggregate_nsdmi 201304L (C++14) 집계 클래스 기본 멤버 초기화

결함 보고서

다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
CWG 80 C++98 모든 데이터 멤버가 클래스 이름과 동일한 이름을 가질 수 없음
(C 호환성 깨짐)
사용자 선언 생성자가 없는 경우
비정적 데이터 멤버가 클래스 이름을
공유하는 것을 허용
CWG 190 C++98 레이아웃 호환성 결정 시
모든 멤버가 고려됨
비정적 데이터 멤버만
고려
CWG 613 C++98 비정적 데이터 멤버의 평가되지 않은 사용이 허용되지 않음 이러한 사용이 허용됨
CWG 645 C++98 비트 필드와 비트 필드가 아닌 멤버의 레이아웃 호환성 여부가 명시되지 않음 레이아웃 호환되지 않음
CWG 1397 C++11 기본 멤버 초기화자에서 클래스가 완전한 것으로 간주됨 기본 멤버 초기화가 기본 생성자의
정의를 트리거할 수 없음
CWG 1425 C++98 표준 레이아웃 객체가 첫 번째 비정적 데이터 멤버와
동일한 주소를 공유하는지, 아니면 첫 번째 기본 클래스
하위 객체와 공유하는지 불명확함
비정적 데이터 멤버가 있으면 해당 멤버,
그렇지 않으면 기본 클래스 하위 객체가
있으면 해당 객체
CWG 1696 C++98 참조 멤버를 임시 객체로 초기화할 수 있음
(해당 임시 객체의 수명은 생성자 종료 시 끝남)
이러한 초기화는 올바르지 않음
CWG 1719 C++98 다른 cv 한정자를 가진 타입들은 레이아웃 호환되지 않음 cv 한정자는 무시되며, 사양이 개선됨
CWG 2254 C++11 데이터 멤버가 없는 표준 레이아웃 클래스에 대한 포인터를
첫 번째 기본 클래스로 reinterpret_cast 할 수 있음
모든 기본 클래스로
reinterpret_cast 할 수 있음
CWG 2583 C++11 공통 초기 시퀀스가 정렬 요구 사항을
고려하지 않음
고려됨
CWG 2759 C++20 공통 초기 시퀀스에 [[ no_unique_address ]] 로 선언된
멤버들이 포함될 수 있음
포함되지 않음

참고 항목

classes
static members
non-static member functions
타입이 standard-layout 타입인지 확인
(클래스 템플릿)
standard-layout 타입의 시작부터 지정된 멤버까지의 바이트 오프셋
(함수 매크로)