Object
C++ 프로그램은 객체 를 생성, 파괴, 참조, 접근 및 조작합니다.
객체는 C++에서
-
크기 (
sizeof로 확인 가능); -
정렬 요구 사항 (
alignof로 확인 가능); - 저장 기간 (자동, 정적, 동적, 스레드 지역);
- 수명 (저장 기간 또는 임시 객체에 의해 제한됨);
- 타입 ;
- 값 (예: 기본 초기화된 비클래스 타입의 경우 불확정적일 수 있음);
- 선택적으로, 이름 .
다음 개체들은 객체가 아닙니다: 값, 참조, 함수, 열거자, 타입, 비정적 클래스 멤버, 템플릿, 클래스 또는 함수 템플릿 특수화, 네임스페이스, 매개변수 팩, 그리고 this .
변수 는 비정적 데이터 멤버가 아닌, 선언 에 의해 도입된 객체나 참조입니다.
목차객체 생성객체는 정의 , new 표현식 , throw 표현식 , union 의 활성 멤버 변경, 그리고 임시 객체 를 필요로 하는 표현식 평가를 통해 명시적으로 생성될 수 있습니다. 생성된 객체는 명시적 객체 생성에서 고유하게 정의됩니다. 암시적 수명 타입 의 객체들은 다음에 의해서도 암시적으로 생성될 수 있습니다
프로그램에 정의된 동작을 부여하는 한, 동일한 저장 영역에 0개 이상의 객체가 생성될 수 있습니다. 이러한 생성이 불가능한 경우(예: 충돌하는 연산으로 인해) 프로그램의 동작은 정의되지 않습니다. 여러 개의 이러한 암시적으로 생성된 객체 집합이 프로그램에 정의된 동작을 부여할 수 있다면, 어떤 객체 집합이 생성되는지 명시되지 않습니다. 즉, 암시적으로 생성된 객체가 고유하게 정의될 필요는 없습니다. 지정된 저장 영역 내에서 객체를 암묵적으로 생성한 후, 일부 연산은 적합하게 생성된 객체 에 대한 포인터를 생성합니다. 적합하게 생성된 객체는 저장 영역과 동일한 주소를 가집니다. 마찬가지로, 프로그램에 정의된 동작을 제공할 수 있는 포인터 값이 존재하지 않는 경우에만 동작이 정의되지 않으며, 프로그램에 정의된 동작을 제공하는 여러 값이 존재할 경우 어떤 포인터 값이 생성되는지 명시되지 않습니다. #include <cstdlib> struct X { int a, b; }; X* MakeX() { // 가능한 정의된 동작 중 하나: // std::malloc 호출은 암시적으로 X 타입의 객체와 // 그 서브객체 a와 b를 생성하고, 해당 X 객체에 대한 포인터를 반환합니다 X* p = static_cast<X*>(std::malloc(sizeof(X))); p->a = 1; p->b = 2; return p; } std::allocator::allocate 호출 또는 union 타입의 암시적으로 정의된 복사/이동 특수 멤버 함수도 객체를 생성할 수 있습니다. 객체 표현과 값 표현일부 타입과 객체는 object representations 와 value representations 를 가지며, 이들은 아래 표에 정의되어 있습니다:
타입이나 객체의 객체 표현에서 값 표현의 일부가 아닌 비트들은 패딩 비트 입니다. TriviallyCopyable 타입의 경우, 값 표현(value representation)은 객체 표현(object representation)의 일부입니다. 이는 저장소에서 객체가 차지하는 바이트들을 복사하는 것만으로 동일한 값을 가진 다른 객체를 생성할 수 있음을 의미합니다(단, 객체가 잠재적으로 중첩되는 부분 객체(subobject)이거나, 값이 해당 타입의 trap representation 이며 이를 CPU에 로드할 때 SNaN("signalling not-a-number") 부동소수점 값이나 NaT("not-a-thing") 정수와 같은 하드웨어 예외가 발생하는 경우는 제외). 대부분의 구현에서는 트랩 표현(trap representation), 패딩 비트(padding bits), 또는 정수 타입에 대한 다중 표현(multiple representations)을 허용하지 않지만, 예외가 있습니다; 예를 들어 Itanium의 정수 타입 값은 트랩 표현일 수 있습니다 . 역은 반드시 참이 아닙니다: 서로 다른 객체 표현을 가진 TriviallyCopyable 타입의 두 객체가 동일한 값을 나타낼 수 있습니다. 예를 들어, 여러 부동 소수점 비트 패턴이 동일한 특수 값 NaN 을 나타낼 수 있습니다. 더 일반적으로는 패딩 비트가 정렬 요구 사항 , 비트 필드 크기 등을 충족시키기 위해 도입될 수 있습니다. #include <cassert> struct S { char c; // 1바이트 값 // 3바이트 패딩 비트 (alignof(float) == 4 가정) float f; // 4바이트 값 (sizeof(float) == 4 가정) bool operator==(const S& arg) const // 값 기반 동등성 비교 { return c == arg.c && f == arg.f; } }; void f() { assert(sizeof(S) == 8); S s1 = {'a', 3.14}; S s2 = s1; reinterpret_cast<unsigned char*>(&s1)[2] = 'b'; // 일부 패딩 비트 수정 assert(s1 == s2); // 값은 변경되지 않음 } char , signed char , 그리고 unsigned char 타입의 객체들에 대해 (과다 크기의 비트 필드 가 아닌 경우), 객체 표현의 모든 비트는 값 표현에 참여해야 하며, 가능한 모든 비트 패턴은 서로 다른 값을 나타내야 합니다 (패딩 비트, 트랩 비트 또는 다중 표현이 허용되지 않음). 서브오브젝트객체는 하위 객체(subobjects) 를 가질 수 있습니다. 여기에는 다음이 포함됩니다
다른 객체의 하위 객체가 아닌 객체를 완전 객체(complete object) 라고 합니다. 완전한 객체, 멤버 하위 객체, 또는 배열 요소가 class type 인 경우, 그 타입은 기반 클래스 하위 객체의 클래스 타입과 구분하기 위해 most derived class 로 간주됩니다. most derived class 타입의 객체 또는 비클래스 타입의 객체는 most derived object 라고 불립니다. 클래스의 경우, 이를 잠재적으로 생성된 서브객체 라고 합니다. 크기
서브오브젝트는 베이스 클래스 서브오브젝트이거나
객체 obj 가 오직 다음과 같은 모든 조건을 만족할 때에만 크기가 0일 가능성이 있습니다:
위의 모든 조건을 만족하는 객체 obj 에 대해:
자세한 내용은 empty base optimization 를 참조하십시오. 크기가 0이 아닌 모든 비트 필드가 아닌 객체는 하나 이상의 바이트 저장 공간을 차지해야 하며, 여기에는 해당 객체의 하위 객체들에 의해 (전체 또는 일부로) 점유되는 모든 바이트가 포함됩니다. 객체가 trivially copyable 또는 standard-layout (since C++11) 타입인 경우, 점유된 저장 공간은 연속적이어야 합니다. 주소객체가 비트 필드(bit-field)가 아니거나 크기가 0인 하위 객체가 아닌 경우, 해당 객체의 주소 는 객체가 차지하는 첫 번째 바이트 의 주소입니다. 객체는 다른 객체를 포함할 수 있으며, 이 경우 포함된 객체는 이전 객체 내에 중첩된 것입니다. 다음 조건 중 하나라도 충족되면 객체 a 가 다른 객체 b 내에 중첩됩니다:
객체는 다음 객체 중 하나인 경우 잠재적으로 고유하지 않은 객체 입니다:
비트 필드가 아닌 두 객체의 수명 이 겹치는 경우:
// 문자 리터럴은 항상 고유함 static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // 항상 true // "r", "s", "il"에서 접근하는 문자 'x'는 // 동일한 주소를 가질 수 있음 (즉, 이 객체들이 저장 공간을 공유할 수 있음) static const char (&r) [] = "x"; static const char *s = "x"; static std::initializer_list<char> il = {'x'}; const bool b2 = r != il.begin(); // 지정되지 않은 결과 const bool b3 = r != s; // 지정되지 않은 결과 const bool b4 = il.begin() != &test1; // 항상 true const bool b5 = r != &test1; // 항상 true 다형성 객체
적어도 하나의 가상 함수를 선언하거나 상속하는 클래스 타입의 객체는 다형성 객체입니다. 각 다형성 객체 내부에서 구현체는 (모든 현존 구현에서 최적화되지 않는 한 하나의 포인터입니다) 추가 정보를 저장하며, 이 정보는
가상 함수
호출과 RTTI 기능(
다형성이 없는 객체의 경우, 값의 해석은 객체가 사용된 표현식에 따라 결정되며 컴파일 시간에 결정됩니다.
이 코드 실행
#include <iostream> #include <typeinfo> struct Base1 { // 다형성 타입: 가상 멤버 선언 virtual ~Base1() {} }; struct Derived1 : Base1 { // 다형성 타입: 가상 멤버 상속 }; struct Base2 { // 비다형성 타입 }; struct Derived2 : Base2 { // 비다형성 타입 }; int main() { Derived1 obj1; // Derived1 타입으로 생성된 object1 Derived2 obj2; // Derived2 타입으로 생성된 object2 Base1& b1 = obj1; // b1은 obj1 객체를 참조 Base2& b2 = obj2; // b2는 obj2 객체를 참조 std::cout << "Expression type of b1: " << typeid(decltype(b1)).name() << '\n' << "Expression type of b2: " << typeid(decltype(b2)).name() << '\n' << "Object type of b1: " << typeid(b1).name() << '\n' << "Object type of b2: " << typeid(b2).name() << '\n' << "Size of b1: " << sizeof b1 << '\n' << "Size of b2: " << sizeof b2 << '\n'; } 가능한 출력: Expression type of b1: Base1 Expression type of b2: Base2 Object type of b1: Derived1 Object type of b2: Base2 Size of b1: 8 Size of b2: 1 엄격한 별칭 지정
객체를 생성 시 사용된 타입이 아닌 다른 타입의 표현식을 사용하여 접근하는 것은 많은 경우에서 정의되지 않은 동작입니다. 예외 사항과 예제 목록은
정렬모든 객체 유형에는 정렬 요구 사항이라는 속성이 있습니다. 이는 음이 아닌 정수 값(std::size_t 유형이며 항상 2의 거듭제곱)으로, 이 유형의 객체를 할당할 수 있는 연속된 주소 사이의 바이트 수를 나타냅니다.
각 객체 유형은 해당 유형의 모든 객체에 자체 정렬 요구 사항을 부과합니다; 더 엄격한 정렬(더 큰 정렬 요구 사항)은 클래스의 모든 비정적 멤버의 정렬 요구 사항을 충족시키기 위해 일부 멤버 뒤에 패딩 비트가 삽입될 수 있습니다. 이 코드 실행 #include <iostream>
// objects of type S can be allocated at any address
// because both S.a and S.b can be allocated at any address
struct S
{
char a; // size: 1, alignment: 1
char b; // size: 1, alignment: 1
}; // size: 2, alignment: 1
// objects of type X must be allocated at 4-byte boundaries
// because X.n must be allocated at 4-byte boundaries
// because int's alignment requirement is (usually) 4
struct X
{
int n; // size: 4, alignment: 4
char c; // size: 1, alignment: 1
// three bytes of padding bits
}; // size: 8, alignment: 4
int main()
{
std::cout << "alignof(S) = " << alignof(S) << '\n'
<< "sizeof(S) = " << sizeof(S) << '\n'
<< "alignof(X) = " << alignof(X) << '\n'
<< "sizeof(X) = " << sizeof(X) << '\n';
}
가능한 출력: alignof(S) = 1
sizeof(S) = 2
alignof(X) = 4
sizeof(X) = 8
가장 약한 정렬(가장 작은 정렬 요구 사항)은 특정 컨텍스트에서 특정 확장 정렬에 대한 요청이 구현에서 지원되지 않는 경우 프로그램은 잘못된 형식(ill-formed)입니다.
참고 사항C++의 객체는 객체 지향 프로그래밍(OOP) 에서의 객체와는 다른 의미를 가집니다:
결함 보고서
P0593R6
에서는 바이트 배열을 생성하거나
할당 함수
(사용자 정의 가능하며
결함 보고서다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
참고 항목
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||