Non-static data members
비정적 데이터 멤버는 클래스의 멤버 명세 에서 선언됩니다.
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비트 비트 필드 };
다음을 제외한 모든 단순 선언 이 허용됩니다:
|
(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) |
두 표준 레이아웃 비-공용체 클래스 타입의 공통 초기 시퀀스 는 각 클래스의 첫 번째 비정적 데이터 멤버와 비트 필드부터 시작하여, 선언 순서대로 배열된 비정적 데이터 멤버와 비트 필드의 가장 긴 시퀀스로서 다음 조건을 만족합니다:
|
(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)을 결정할 수 있습니다.
-
표준 레이아웃(standard-layout) 공용체에서 비공용체 클래스 타입
멤버 초기화
비정적 데이터 멤버는 두 가지 방법 중 하나로 초기화될 수 있습니다:
|
2)
기본 멤버 초기화자(default member initializer)
를 통해, 이는 멤버 선언에 포함된 중괄호 또는 등호
초기화자(initializer)
이며, 생성자의 멤버 초기화 목록에서 해당 멤버가 생략된 경우 사용됩니다.
struct S { int n = 7; std::string s{'a', 'b', 'c'}; S() {} // 기본 멤버 초기화자가 n을 복사 초기화하고, s를 목록 초기화합니다 }; 멤버에 기본 멤버 초기화자가 있고 생성자의 멤버 초기화 목록에도 나타나는 경우, 해당 생성자에 대해서는 기본 멤버 초기화자가 무시됩니다.
이 코드 실행
배열 타입의 멤버는 멤버 초기화자로부터 크기를 추론할 수 없습니다: 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부터) |
사용법
비정적 데이터 멤버 또는 비정적 멤버 함수의 이름은 다음 세 가지 상황에서만 나타날 수 있습니다:
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(); };
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
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
참고 사항
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__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 | |
|
(C++11)
|
타입이
standard-layout
타입인지 확인
(클래스 템플릿) |
|
standard-layout
타입의 시작부터 지정된 멤버까지의 바이트 오프셋
(함수 매크로) |
|