다음을 통해 공유


이니셜라이저

이니셜라이저는 변수의 초기 값을 지정합니다. 변수를 초기화할 수 있는 컨텍스트는 다음과 같습니다.

  • 변수 정의

    int i = 3;
    Point p1{ 1, 2 };
    
  • 함수 매개 변수 중 하나

    set_point(Point{ 5, 6 });
    
  • 함수의 반환 값으로서 초기화

    Point get_new_point(int x, int y) { return { x, y }; }
    Point get_new_point(int x, int y) { return Point{ x, y }; }
    

이니셜라이저가 사용할 수 있는 폼은 다음과 같습니다.

  • 괄호 안에 있는 식(또는 쉼표로 구분된 식 목록)

    Point p1(1, 2);
    
  • 등호 뒤에 식

    string s = "hello";
    
  • 중괄호로 묶인 이니셜라이저 목록 목록은 비워져 있거나 목록 집합으로 구성될 수 있습니다.

    struct Point{
        int x;
        int y;
    };
    class PointConsumer{
    public:
        void set_point(Point p){};
        void set_points(initializer_list<Point> my_list){};
    };
    int main() {
        PointConsumer pc{};
        pc.set_point({});
        pc.set_point({ 3, 4 });
        pc.set_points({ { 3, 4 }, { 5, 6 } });
    }
    

초기화의 종류

초기화는 여러 종류가 있으며 프로그램 실행의 여러 지점에서 발생할 수 있습니다. 다양한 종류의 초기화는 상호 배타적이지 않습니다. 예를 들어 목록 초기화는 값 초기화를 트리거할 수 있고 다른 상황에서는 집합체 초기화를 트리거할 수 있습니다.

0 초기화

0 초기화는 변수를 암시적으로 형식으로 변환된 0으로 설정하는 것입니다.

  • 숫자 변수는 0(또는 0.0 또는 0.0000000000 등)으로 초기화됩니다.

  • Char 변수는 ‘\0’으로 초기화됩니다.

  • 포인터는 nullptr로 초기화됩니다.

  • 배열, POD 클래스, 구조체 및 공용 구조체는 멤버 값을 0으로 초기화합니다.

0 초기화는 다음과 같은 다양한 시간에 수행됩니다.

  • 프로그램 시작 시 - 정적 지속 시간이 있는 명명된 모든 변수 이러한 변수는 나중에 다시 초기화할 수 있습니다.

  • 값을 초기화하는 동안 - 빈 중괄호를 사용하여 초기화된 스칼라 형식 및 POD 클래스 형식

  • 멤버의 하위 집합만 초기화된 어레이

0 초기화의 몇 가지 예제는 다음과 같습니다.

struct my_struct{
    int i;
    char c;
};

int i0;              // zero-initialized to 0
int main() {
    static float f1;  // zero-initialized to 0.000000000
    double d{};     // zero-initialized to 0.00000000000000000
    int* ptr{};     // initialized to nullptr
    char s_array[3]{'a', 'b'};  // the third char is initialized to '\0'
    int int_array[5] = { 8, 9, 10 };  // the fourth and fifth ints are initialized to 0
    my_struct a_struct{};   // i = 0, c = '\0'
}

기본 초기화

기본 생성자를 사용하는 클래스, 구조체 및 공용 구조체에 대한 기본 초기화입니다. 기본 생성자는 초기화 식을 포함하지 않거나 new 키워드를 사용하여 호출할 수 있습니다.

MyClass mc1;
MyClass* mc3 = new MyClass;

클래스, 구조체 또는 공용 구조체에 기본 생성자가 있으면 컴파일러가 오류를 내보냅니다.

스칼라 변수는 초기화 식 없이 정의되면 기본값으로 초기화됩니다. 비활성화 상태 값입니다.

int i1;
float f;
char c;

배열은 초기화 식 없이 정의되면 기본값으로 초기화됩니다. 배열이 기본값으로 초기화되면 멤버가 기본값으로 초기화되고 비활성화 상태 값입니다.

int int_arr[3];

배열에 기본 생성자가 없는 경우, 컴파일러가 오류를 내보냅니다.

상수 변수의 기본 초기화

상수 변수는 이니셜라이저와 함께 선언해야 합니다. 스칼라 형식인 경우 컴파일러 오류가 발생되고 기본 생성자가 있는 클래스 형식인 경우 경고가 발생됩니다.

class MyClass{};
int main() {
    //const int i2;   // compiler error C2734: const object must be initialized if not extern
    //const char c2;  // same error
    const MyClass mc1; // compiler error C4269: 'const automatic data initialized with compiler generated default constructor produces unreliable results
}

정적 변수의 기본 초기화

이니셜라이저 없이 선언된 정적 변수는 0으로 초기화됩니다(형식으로 암시적으로 변환).

class MyClass {   
private:
    int m_int;
    char m_char;
};

int main() {
    static int int1;       // 0
    static char char1;     // '\0'
    static bool bool1;   // false
    static MyClass mc1;     // {0, '\0'}
}

전역 정적 개체의 초기화에 대한 자세한 내용은 추가 시작 고려 사항를 참조하십시오.

값 초기화

값 초기화는 다음과 같은 경우에 이루어집니다.

  • 명명된 값은 빈 중괄호 초기화를 사용하여 초기화됩니다.

  • 익명의 임시 개체는 빈 괄호나 중괄호를 사용하여 초기화됩니다.

  • 개체는 new 키워드와 빈 괄호나 중괄호를 사용하여 초기화됩니다.

값 초기화가 수행하는 작업은 다음과 같습니다.

  • 적어도 하나 이상의 공용 생성자가 있는 클래스의 경우 기본 생성자가 호출됩니다.

  • 선언된 생성자가 없는 공용 구조체가 아닌 클래스의 경우 개체가 0으로 초기화되고 기본 생성자가 호출됩니다.

  • 배열의 경우 모든 요소의 값이 초기화됩니다.

  • 다른 모든 경우에는 변수가 0으로 초기화됩니다.

class BaseClass {  
private:
    int m_int;
};

int main() {
    BaseClass bc{};     // class is initialized
    BaseClass*  bc2 = new BaseClass();  // class is initialized, m_int value is 0
    int int_arr[3]{};  // value of all members is 0
    int a{};     // value of a is 0
    double b{};  // value of b is 0.00000000000000000
}

복사 초기화

복사 초기화는 하나의 개체를 다른 개체를 사용하여 초기화하는 것입니다. 다음과 같은 경우에 이루어집니다.

  • 변수는 등호를 사용하여 초기화됩니다.

  • 인수는 함수에 전달됩니다.

  • 개체는 함수에서 반환됩니다.

  • 예외가 throw되거나 catch됩니다.

  • 비정적 데이터 멤버는 등호를 사용하여 초기화됩니다.

  • 클래스, 구조체 및 공용 구조체 멤버는 집합체 초기화 중에 복사 초기화로 초기화됩니다. 예제는 집합체 초기화를 참조하십시오.

이 코드는 복사 초기화의 예제를 보여 줍니다.

#include <iostream>
using namespace std;

class MyClass{
public:
    MyClass(int myInt) {}
    void set_int(int myInt) { m_int = myInt; }
    int get_int() const { return m_int; }
private:
    int m_int = 7; // copy initialization of m_int

};
class MyException : public exception{};
int main() {
    int i = 5;              // copy initialization of i
    MyClass mc1{ i };
    MyClass mc2 = mc1;      // copy initialization of mc2 from mc1
    MyClass mc1.set_int(i);    // copy initialization of parameter from i
    int i2 = mc2.get_int(); // copy initialization of i2 from return value of get_int()

    try{
        throw MyException();    
    }
    catch (MyException ex){ // copy initialization of ex
        cout << ex.what();  
    }
}

복사 초기화는 명시적 생성자를 호출할 수 없습니다.

vector<int> v = 10; // the constructor is explicit; compiler error C2440: cannot convert from 'int' to 'std::vector<int,std::allocator<_Ty>>'
regex r = "a.*b"; // the constructor is explicit; same error
shared_ptr<int> sp = new int(1729); // the constructor is explicit; same error

경우에 따라 클래스의 복사 생성자가 삭제되거나 액세스할 수 없는 경우 복사 초기화로 컴파일러 오류가 발생합니다. 자세한 내용은 명시적 초기화을 참조하십시오.

직접 초기화

직접 초기화는 (비어 있지 않은) 중괄호 또는 괄호를 사용합니다. 복사 초기화와는 달리 명시적 생성자를 호출할 수 있습니다. 다음과 같은 경우에 이루어집니다.

  • 변수는 비어 있지 않은 중괄호 또는 괄호를 사용하여 초기화됩니다.

  • 변수는 new 키워드와 비어 있지 않은 중괄호 또는 괄호를 사용하여 초기화됩니다.

  • 변수는 static_cast를 사용하여 초기화됩니다.

  • 생성자에서 기본 클래스 및 비정적 멤버는 이니셜라이저 목록을 사용하여 초기화됩니다.

  • 람다 식에서 캡처된 변수의 복사본

직접 초기화의 예제는 다음과 같습니다.

class BaseClass{
public:
    BaseClass(int n) :m_int(n){} // m_int is direct initialized
private:
    int m_int;
};

class DerivedClass : public BaseClass{
public:
    // BaseClass and m_char are direct initialized
    DerivedClass(int n, char c) : BaseClass(n), m_char(c) {}
private:
    char m_char;
};
int main(){
    BaseClass bc1(5);
    DerivedClass dc1{ 1, 'c' };
    BaseClass* bc2 = new BaseClass(7);
    BaseClass bc3 = static_cast<BaseClass>(dc1);

    int a = 1;
    function<int()> func = [a](){  return a + 1; }; // a is direct initialized
    int n = func();
}

목록 초기화

목록 초기화는 변수가 중괄호로 묶인 이니셜라이저 목록을 사용하여 초기화되면 이루어집니다. 중괄호로 묶인 이니셜라이저 목록이 사용되는 경우는 다음과 같습니다.

  • 변수가 초기화됩니다.

  • 클래스는 new 키워드를 사용하여 초기화됩니다.

  • 개체는 함수에서 반환됩니다.

  • 인수가 함수에 전달됩니다.

  • 직접 초기화의 인수 중 하나

  • 비정적 데이터 멤버 이니셜라이저

  • 생성자 이니셜라이저 목록

목록 초기화의 예제는 다음과 같습니다.

class MyClass {
public:
    MyClass(int myInt, char myChar) {}  
private:
    int m_int[]{ 3 };
    char m_char;
};
class MyClassConsumer{
public:
    void set_class(MyClass c) {}
    MyClass get_class() { return MyClass{ 0, '\0' }; }
};
struct MyStruct{
    int my_int;
    char my_char;
    MyClass my_class;
};
int main() {
    MyClass mc1{ 1, 'a' };
    MyClass* mc2 = new MyClass{ 2, 'b' };
    MyClass mc3 = { 3, 'c' };

    MyClassConsumer mcc;
    mcc.set_class(MyClass{ 3, 'c' });
    mcc.set_class({ 4, 'd' });

    MyStruct ms1{ 1, 'a', { 2, 'b' } };
}

집합체 초기화

집합체 초기화는 다음과 같은 일종의 배열 또는 클래스 형식(대개 구조체 또는 공용 구조체) 목록 초기화입니다.

  • 전용 또는 보호된 멤버 없음

  • 명시적으로 기본값으로 설정되었거나 삭제된 생성자를 제외하고 사용자 제공 생성자 없음

  • 기본 클래스 없음

  • 가상 멤버 함수 없음

  • 비정적 멤버의 경우 중괄호 또는 등호 이니셜라이저 없음

집합체 이니셜라이저는 중괄호로 묶은 초기화 목록으로 구성되며, 등호가 있을 수도 있고 없을 수도 있습니다.

#include <iostream>
using namespace std;

struct MyAggregate{
    int myInt;
    char myChar;
};

int main() {
    MyAggregate agg1{ 1, 'c' };

    cout << "agg1: " << agg1.myChar << ": " << agg1.myInt << endl;
    cout << "agg2: " << agg2.myChar << ": " << agg2.myInt << endl;

    int myArr1[]{ 1, 2, 3, 4 };
    int myArr2[3] = { 5, 6, 7 };
    int myArr3[5] = { 8, 9, 10 };

    cout << "myArr1: ";
    for (int i : myArr1){
        cout << i << " ";
    }
    cout << endl;
    
    cout << "myArr3: ";
    for (auto const &i : myArr3) {
        cout << i << " ";
    }
    cout << endl;
}

출력은 다음과 같습니다.

agg1: c: 1
agg2: d: 2
myArr1: 1 2 3 4
myArr3: 8 9 10 0 0

중요

myArr3의 경우와 같이 집합체 초기화 중에 선언되었지만 명시적으로 초기화되지 않은 배열 멤버

공용 구조체 및 구조체 초기화

공용 구조체에 생성자가 없으면 값을 사용하여(또는 공용 구조체의 다른 인스턴스를 사용하여) 초기화할 수 있습니다. 값을 사용하여 첫 번째 비정적 필드를 초기화합니다. 이는 구조체 초기화와는 다릅니다. 구조체 초기화는 이니셜라이저의 첫 번째 값을 사용하여 첫 번째 필드를 초기화하고, 두 번째 값을 사용하여 두 번째 필드를 초기화하는 식으로 이루어집니다. 이 예제에서는 구조체와 공용 구조체 초기화를 비교해 보십시오.

struct MyStruct {
    int myInt;
    char myChar;
};
union MyUnion {
    int my_int;
    char my_char;
    bool my_bool;
    MyStruct my_struct;
};

int main() {  
    MyUnion mu1{ 'a' };  // my_int = 97, my_char = 'a', my_bool = true, {myInt = 97, myChar = '\0'}
    MyUnion mu2{ 1 };   // my_int = 1, my_char = 'x1', my_bool = true, {myInt = 1, myChar = '\0'}
    MyUnion mu3{};      // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
    MyUnion mu4 = mu3;  // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
    //MyUnion mu5{ 1, 'a', true };  // compiler error: C2078: too many initializers
    //MyUnion mu6 = 'a';            // compiler error: C2440: cannot convert from 'char' to 'MyUnion'
    //MyUnion mu7 = 1;              // compiler error: C2440: cannot convert from 'int' to 'MyUnion'

    MyStruct ms1{ 'a' };            // myInt = 97, myChar = '\0'
    MyStruct ms2{ 1 };              // myInt = 1, myChar = '\0'
    MyStruct ms3{};                 // myInt = 0, myChar = '\0'
    MyStruct ms4{1, 'a'};           // myInt = 1, myChar = 'a'
    MyStruct ms5 = { 2, 'b' };      // myInt = 2, myChar = 'b'
}

집합체가 포함된 집합체 초기화

집합체 형식은 배열의 배열, 구조체의 배열 등 다른 집합체 형식을 포함할 수 있습니다. 이러한 형식은 중첩된 중괄호 집합을 사용하여 초기화됩니다.

struct MyStruct {
    int myInt;
    char myChar;
};
int main() {
    int intArr1[2][2]{{ 1, 2 }, { 3, 4 }};
    int intArr3[2][2] = {1, 2, 3, 4};  
    MyStruct structArr[]{ { 1, 'a' }, { 2, 'b' }, {3, 'c'} };
}

참조 초기화

참조 초기화에 대한 자세한 내용은 참조 초기화를 참조하십시오.

외부 변수 초기화

자동, 등록, 정적 및 외부 변수의 선언은 이니셜라이저를 포함할 수 있습니다. 하지만 외부 변수 선언은 변수가 extern으로 선언되지 않은 경우에만 이니셜라이저를 포함할 수 있습니다. 자세한 내용은 External을 참조하십시오.

참고 항목

참조

선언자