Dominic Hamon (dominic@google.com) 작성
최초 게시일: 2018년 4월 19일
최종 업데이트: 2020년 4월 6일
빠른 링크: abseil.io/tips/146
“성공으로 가는 길은 항상 공사 중이다.”
– 릴리 톰린
요약
안전성과 가독성을 위해, 스칼라(scalar) 객체는 명시적으로 값을 설정할 때까지 적절한 값으로 초기화되지 않는다고 가정하세요. 초기화를 사용하면 스칼라 값을 안전한 값으로 설정할 수 있습니다.
소개
객체가 생성될 때, 초기화될 수도 있고 초기화되지 않을 수도 있습니다. 초기화되지 않은 객체를 읽는 것은 안전하지 않으며, 객체가 초기화되지 않았는지 이해하는 것은 쉽지 않습니다.
초기화 여부를 이해하려면 먼저 생성 중인 타입이 스칼라(scalar), 집계(aggregate), 혹은 다른 타입인지 알아야 합니다.
- 스칼라 타입: 정수형, 부동소수점 숫자, 포인터, 열거형(enum), 멤버 포인터,
nullptr_t
등 간단한 타입. - 집계 타입: 배열, 가상 멤버 없음, 비공개 필드 또는 기반 클래스 없음, 생성자 선언 없음 등의 특성을 가진 클래스.
또한, 객체가 안전하게 읽을 수 있는 값으로 초기화되었는지 여부는 명시적인 초기화자(initializer)를 사용했는지에 따라 달라집니다. 초기화자는 ()
, {}
, 또는 = {}
형태로 객체 이름 뒤에 나옵니다.
이 규칙은 직관적이지 않으므로, 객체가 초기화되었는지 보장하려면 초기화자를 제공하는 것이 가장 쉬운 방법입니다. 이를 값 초기화(value-initialization)라고 하며, 컴파일러가 스칼라 및 집계 타입에 대해 수행하는 기본 초기화(default-initialization)와는 다릅니다.
사용자 정의 생성자
사용자 정의 생성자가 정의된 타입은 집계 타입이 아니며, 값 초기화와 기본 초기화가 모두 생성자를 호출하므로 초기화가 더 간단합니다:
struct Foo {
Foo() : v() {}
int v;
std::string s;
};
int main() {
Foo default_foo;
Foo value_foo = {};
}
위 코드에서 = {}
는 value_foo
의 값 초기화를 트리거하며, 이는 Foo
의 기본 생성자를 호출합니다. 생성자 초기화 리스트가 v
를 값 초기화하므로, v
는 안전하게 읽을 수 있습니다. v
는 클래스 타입이 아니므로 이는 제로 초기화(zero-initialization)라는 값 초기화의 특별한 경우로, value_foo.v
는 0
이 됩니다.
사용자 선언 생성자와 사용자 정의 생성자
다음 코드처럼 생성자를 = default
로 선언할 수 있습니다:
struct Foo {
Foo() = default;
int v;
};
int main() {
Foo default_foo;
Foo value_foo = {};
}
이 경우, Foo
는 사용자 선언(user-declared) 생성자를 가지지만, 사용자 정의(user-provided) 생성자는 아닙니다. 따라서 default_foo.v
는 초기화되지 않고, value_foo.v
는 제로 초기화됩니다.
명시적 값 초기화
값을 명시적으로 초기화하는 것이 독자를 위해 더 좋은 선택입니다. 예:
struct Foo {
Foo() : v(0) {}
int v;
};
기본 멤버 초기화
멤버 변수를 선언 시 초기화하면, 기본 초기화와 값 초기화의 혼란을 방지할 수 있습니다:
struct Foo {
int v = 0;
};
프로 팁: 스칼라 제로 초기화
스칼라 값이 안전하게 초기화되는 조건:
()
,{}
, 또는= {}
와 같은 명시적 초기화자 사용.- 배열 요소의 초기화자 사용. 예:
new int[10]();
. - 클래스 멤버가 비활성 기본 생성자를 가지며 외부 객체가 값 초기화된 경우.
- 정적 또는 스레드 로컬 인스턴스.
- 집계 타입의 멤버가 초기화된 경우.
권장 사항
- 스칼라 타입 초기화에 명시적으로 값을 설정하세요.
- 초기화 또는 값 할당 전에는 모든 스칼라 타입 인스턴스가 불확정 값이라고 가정하세요.
- 클래스에 여러 생성자가 있고 멤버에 기본값이 있다면 기본 멤버 초기화를 사용하세요.
추가 자료
- Tip #61: Default Member Initializers
- Tip #88: Initialization:
=
,()
, and{}
- Tip #131: Special member functions and
=default
- CppReference - Initialization