생성자 멤버 초기화 리스트 (Constructor member initializer list)
이전 포스트 생성자에서 대입 연산자(=)를 사용하여 클래스 멤버 변수를 초기화했다:
class Something
{
private:
int m_value1;
double m_value2;
char m_value3;
public:
Something()
{
// 이것은 모두 할당이지 초기화가 아니다.
m_value1 = 1;
m_value2 = 2.2;
m_value3 = 'c';
}
};
클래스 생성자가 실행될 때 m_value1, m_value2 및 m_value3 변수가 생성된다. 그런 다음 생성자 본문이 실행되며, 멤버 변수값이 할당된다. 이 순서는 C++에서 다음 코드와 흐름이 비슷하다.
int m_value1;
double m_value2;
char m_value3;
m_value1 = 1;
m_value2 = 2.2;
m_value3 = 'c';
이 순서는 C++ 언어의 문법에서는 유효하지만, 초기화보다 효율적이지 않다.
그리고 일부 유형의 데이터(const 및 참조 변수)는 선언과 동시에 초기화해야 한다:
class Something
{
private:
const int m_value;
public:
Something()
{
m_value = 1; // error: const 상수 변수는 할당할 수 없다.
}
};
다음과 유사한 코드가 생성된다:
const int m_value; // error: const 상수 변수는 값으로 초기화되어야 한다.
m_value = 5; // error: const 상수 변수는 할당할 수 없다.
이 문제를 해결하기 위해 C++은 생성자 멤버 초기화 리스트 (Constructor member initializer list)을 통해 멤버 변수를 초기화하는 방법을 제공한다.
여러 가지 변수 초기화 방법 포스트에서 변수를 초기화하는 세 가지 방법을 배웠다:
- 복사 초기화 (copy initialization)
- 직접 초기화 (direct initialization)
- 유니폼 초기화 (uniform initialization)
int value1 = 1; // 복사 초기화
double value2(2.2); // 직접 초기화
char value3 {'c'}; // 유니폼 초기화
멤버 초기화 리스트를 사용하는 것은 직접 초기화(또는 C++ 11에서는 유니폼 초기화)를 수행하는 것과 유사하다.
class Something
{
private:
int m_value1;
double m_value2;
char m_value3;
public:
Something()
{
// These are all assignments, not initializations
m_value1 = 1;
m_value2 = 2.2;
m_value3 = 'c';
}
};
위와 같은 예제를 이제 초기화 리스트를 사용해서 같은 코드를 작성해보겠다:
class Something
{
private:
int m_value1;
double m_value2;
char m_value3;
public:
Something() : m_value1(1), m_value2(2.2), m_value3('c') // 멤버 초기화 리스트를 통해 멤버 변수를 직접 초기화할 수 있다.
{
// 멤버 변수를 여기서 할당할 필요가 없다.
}
void print()
{
std::cout << "Something(" << m_value1 << ", " << m_value2 << ", " << m_value3 << ")\n";
}
};
int main()
{
Something something;
something.print();
return 0;
}
This prints:
Something(1, 2.2, c)
멤버 초기화 리스트는 생성자 매개 변수 뒤에 삽입된다. 콜론 :
으로 시작한 다음 초기화 할 각 변수를 쉼표로 구분하여 해당 변수의 값과 함께 나열한다. 또한, 초기화 리스트는 세미콜론 ;
으로 끝나지 않는다.
Something() : m_value1(1), m_value2(2.2), m_value3('c')
{
// ...
// No need for assignment here
}
초기화 리스트가 멤버 변수 초기화 기능을 대체하므로 더는 생성자 본문에서 할당을 수행할 필요가 없다.
초기화 리스트는 호출자가 초기화 값을 전달할 때 더 유용하다:
#include <iostream>
class Something
{
private:
int m_value1;
double m_value2;
char m_value3;
public:
Something(int value1, double value2, char value3='c')
: m_value1(value1), m_value2(value2), m_value3(value3) // 멤버 초기화 리스트를 통해 멤버 변수를 직접 초기화할 수 있다.
{
// 멤버 변수를 여기서 할당할 필요가 없다.
}
void print()
{
std::cout << "Something(" << m_value1 << ", " << m_value2 << ", " << m_value3 << ")\n";
}
};
int main()
{
Something something(1, 2.2); // value1 = 1, value2=2.2, value3 gets default value 'c'
something.print();
return 0;
}#include <iostream>
class Something
{
private:
int m_value1;
double m_value2;
char m_value3;
public:
Something(int value1, double value2, char value3='c')
: m_value1(value1), m_value2(value2), m_value3(value3) // 멤버 초기화 리스트를 통해 멤버 변수를 직접 초기화할 수 있다.
{
// 멤버 변수를 여기서 할당할 필요가 없다.
}
void print()
{
std::cout << "Something(" << m_value1 << ", " << m_value2 << ", " << m_value3 << ")\n";
}
};
int main()
{
Something something(1, 2.2); // value1 = 1, value2=2.2, value3 gets default value 'c'
something.print();
return 0;
}
This prints:
Something(1, 2.2, c)
C++ 11의 유니폼 초기화
C++ 11에서는 직접 초기화 대신 유니폼 초기화를 사용할 수 있다:
class Something
{
private:
const int m_value;
public:
Something(): m_value { 5 } // 멤버 초기화 리스트를 통해 멤버 변수를 유니폼 초기화 할 수도 있다.
{
}
};
나중에 상속을 수행할 때 초기화 리스트가 필요하므로 const 및 reference 변수를 사용하지 않더라도 이 초기화 리스트를 사용하는 게 좋다.
초기화 리스트를 사용한 배열 멤버 초기화
배열 멤버가 있는 클래스를 보자:
class Something
{
private:
const int m_array[5];
};
C++ 11 이전에는 멤버 초기화 리스트를 통해서 배열 멤버를 0으로만 만들 수 있었다:
class Something
{
private:
const int m_array[5];
public:
Something(): m_array {} // zero the member array
{
}
};
그러나 C++ 11에서는 유니폼 초기화를 사용하여 멤버 배열을 완전히 초기화할 수 있다:
클래스인 멤버 변수 초기화
멤버 초기화 리스트를 사용해서 클래스인 멤버를 초기화할 수 있다:
#include <iostream>
class A
{
public:
A(int x) { std::cout << "A " << x << "\n"; }
};
class B
{
private:
A m_a;
public:
B(int y) : m_a(y-1) // call A(int) constructor to initialize member m_a
{
std::cout << "B " << y << "\n";
}
};
int main()
{
B b(5);
return 0;
}
This prints:
A 4
B 5
변수 b가 생성되면 B(int) 생성자가 값 5로 호출된다. 생성자 본문이 실행되기 전에 m_a가 초기화되므로 값 4로 A(int) 생성자가 호출된다. 그러면 "A 4"가 인쇄되고, 그런 다음 제어는 B 생성자로 돌아가므로 B 생성자의 본문이 실행되어 "B 5"를 인쇄한다.
요약
멤버 초기화 리스트를 사용하면 값을 할당하지 않고 멤버 변수를 초기화할 수 있다.
생성자 본문에서 값을 할당하는 것보다 성능이 더 우수하다.
const 또는 reference 변수와 같이 초기값이 필요한 멤버를 초기화할 수 있는 유일한 방법이다.
멤버 초기화 리스트는 기본 자료형 변수와 클래스 자체인 멤버 모두에서 잘 작동한다.
번역: 이 포스트의 원문은 https://www.learncpp.com/cpp-tutorial/8-5a-constructor-member-initializer-lists/ 입니다.