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 초기화로 이끌던 것이, 기본 초기화로 바뀌었어요.

더 알아보기 (Learn more)

cppreference