value_initialization — 값 초기화
value_initialization — 값 초기화 (Value-initialization)
**값 초기화(value-initialization)**는 객체가 빈 초기화자로 생성될 때 수행되는 초기화예요. T() 또는 T{} 형태로 초기화할 때 적용돼요.
객체를 "기본값으로 초기화했을 때의 값"을 얻고 싶을 때 사용해요. 스칼라는 0으로, 클래스는 0과 기본 생성 호출로 초기화되는 규칙이에요.
출처: cppreference
본문
값 초기화는 객체가 빈 초기화자로 생성될 때 수행되는 초기화예요.
문법 (Syntax)
T ()
new T ()
Class::Class(...) : member () { ... }
T object {};
T {}
new T {}
Class::Class(...) : member {} { ... }
설명 (Explanation)
값 초기화는 다음 상황에서 수행돼요.
1,5. 이름 없는 임시 객체가 빈 괄호 또는 중괄호로 이루어진 초기화자로 생성될 때
2,6. 동적 저장 기간 객체가 빈 괄호 또는 중괄호 초기화자로 new 표현식으로 생성될 때
3,7. 비정적 데이터 멤버나 기반 클래스가 빈 괄호 또는 중괄호 초기화자로 멤버 초기화를 통해 초기화될 때
4. 이름 있는 객체(자동, 정적, 스레드 로컬)가 한 쌍의 중괄호로 이루어진 초기화자로 선언될 때
모든 경우에, 빈 중괄호 {}가 사용되고 T가 애그리거트 타입이면 값 초기화 대신 애그리거트 초기화가 수행돼요.
C++11부터, T가 기본 생성자가 없지만 std::initializer_list를 받는 생성자가 있으면 목록 초기화가 수행돼요.
값 초기화의 효과
T가 (가능하면 cv 한정된) 클래스 타입이면:T에 대한 기본 초기화가 생성자를 선택하고 그 생성자가 사용자 제공이 아니라면, 객체는 먼저 **0 초기화(zero-initialized)**돼요.- 어떤 경우든 객체는 기본 초기화(default-initialized)돼요.
T가 배열 타입이면, 배열의 각 요소가 값 초기화돼요.- 그 외에는 객체가 0 초기화돼요.
노트 (Notes)
T object(); 문법은 객체를 초기화하지 않아요. 인자를 받지 않고 T를 반환하는 함수를 선언해요. C++11 이전에 이름 있는 변수를 값 초기화하는 방법은 T object = T();였는데, 이는 임시를 값 초기화한 뒤 객체를 복사 초기화하는 것이고, 대부분의 컴파일러가 이 복사를 최적화해 제거해요.
참조는 값 초기화될 수 없어요. 함수형 캐스트에서 T()는 배열 타입을 이름 짓는 경우 금지되지만, T{}는 허용돼요.
모든 표준 컨테이너(std::vector, std::list 등)는 단일 size_type 인자로 생성되거나, 할당자가 construct의 동작을 커스터마이즈하지 않는다면, resize() 호출로 커졌을 때 요소들을 값 초기화해요.
예제 (Example)
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
struct T1
{
int mem1;
std::string mem2;
virtual void foo() {} // T1 이 애그리거트가 아니게 함
}; // 암시적 기본 생성자
struct T2
{
int mem1;
std::string mem2;
T2(const T2&) {} // 사용자 제공 복사 생성자
}; // 기본 생성자 없음
struct T3
{
int mem1;
std::string mem2;
T3() {} // 사용자 제공 기본 생성자
};
std::string s{}; // 클래스 => 기본 초기화, 값은 ""
int main()
{
int n{}; // 스칼라 => 0 초기화, 값은 0
assert(n == 0);
double f = double(); // 스칼라 => 0 초기화, 값은 0.0
assert(f == 0.0);
int* a = new int[10](); // 배열 => 각 요소 값 초기화
assert(a[9] == 0); // 각 요소의 값은 0
T1 t1{}; // 암시적 기본 생성자의 클래스 =>
assert(t1.mem1 == 0); // t1.mem1 은 0 초기화, 값은 0
assert(t1.mem2 == ""); // t1.mem2 는 기본 초기화, 값은 ""
// T2 t2{}; // 오류: 기본 생성자가 없는 클래스
T3 t3{}; // 사용자 제공 기본 생성자의 클래스 =>
std::cout << t3.mem1; // t3.mem1 은 불확정 값으로 기본 초기화
assert(t3.mem2 == ""); // t3.mem2 는 기본 초기화, 값은 ""
std::vector<int> v(3); // 각 요소 값 초기화
assert(v[2] == 0); // 각 요소의 값은 0
std::cout << '\n';
delete[] a;
}
T1{}은 암시적 기본 생성자라서 멤버가 0 초기화된 뒤 기본 초기화되고, T3{}은 사용자 제공 기본 생성자라서 0 초기화 없이 기본 초기화돼요. 그래서 t3.mem1은 불확정 값을 가질 수 있어요.
결함 보고 (Defect reports)
- C++98에서 값 초기화가 없었고, 빈 초기화자가 기본 초기화를 부르던 것이, 값 초기화를 부르도록 바뀌었어요.
- C++98에서 사용자 제공 생성자가 없는 클래스 객체에 대한 값 초기화가 각 부분 객체를 값 초기화하는 것과 동등하던 것이, 전체 객체를 0 초기화한 뒤 기본 생성자를 호출하도록 바뀌었어요.
- C++11에서 삭제된 기본 생성자를 가진 유니언의 값 초기화가 0 초기화로 이끌던 것이, 기본 초기화로 바뀌었어요.