기본 생성자
기본 생성자 (Default constructor)
기본 생성자(default constructor) 는 인자 없이 호출할 수 있는 생성자예요. T x; 같은 기본 초기화나 T() 같은 값 초기화를 할 때 이 생성자가 호출돼요. 클래스에 생성자를 하나도 안 써 놓으면 컴파일러가 대신 만들어 주기도 하고, 명시적으로 = default로 요청하거나 = delete로 막을 수도 있어요.
출처: cppreference
본문
문법 (Syntax)
기본 생성자를 선언/정의하는 여러 가지 형태가 있어요.
| 표기 | 의미 |
|---|---|
class-name(parameter-list(선택)); |
(1) 클래스 정의 안에서 기본 생성자 선언 |
class-name(parameter-list(선택)) function-body |
(2) 클래스 정의 안에서 기본 생성자 정의 |
class-name() = default; |
(3) 기본 생성자를 명시적으로 defaulted 처리 (C++11부터) |
class-name(parameter-list(선택)) = delete; |
(4) 기본 생성자를 삭제 (C++11부터) |
class-name::class-name(parameter-list(선택)) function-body |
(5) 클래스 정의 밖에서 기본 생성자 정의 (클래스 안에 (1) 선언이 있어야 함) |
class-name::class-name() = default; |
(6) 클래스 정의 밖에서 명시적으로 defaulted 처리 (C++11부터) |
class-name— 기본 생성자를 선언하려는 클래스.parameter-list— 모든 매개변수(매개변수 팩은 제외, C++11부터)가 기본 인자를 가진 매개변수 목록.function-body— 기본 생성자의 함수 본문.
여기서 (1)(4)는 클래스 정의 안에서, (5)(6)은 클래스 정의 밖에서 정의하는 형태예요. (3)이 명시적 defaulted, (4)가 삭제된 정의예요.
기본 생성자는 기본 초기화(default initialization) 와 값 초기화(value initialization) 중에 호출돼요.
암시적으로 선언된 기본 생성자
클래스 타입에 사용자가 선언한 생성자나 생성자 템플릿이 하나도 없다면, 컴파일러는 기본 생성자를 그 클래스의 인라인 public 멤버로 암시적으로 선언해요.
암시적으로 선언된(또는 첫 선언에서 defaulted된) 기본 생성자는 동적 예외 명세(until C++17) / noexcept 명세(since C++17)에 설명된 예외 명세를 가져요.
암시적으로 정의된 기본 생성자
생성자가 암시적으로 선언됐거나(until C++11) 명시적으로 defaulted됐고 deleted로 정의되지 않았다면(since C++11), 그 생성자는 odr-사용(odr-used)되거나 상수 평가에 필요할 때(since C++11) 컴파일러가 암시적으로 정의해요.
- (C++26부터) union-유사 클래스
T의 기본 생성자가 trivial하면,T자신이거나T의 익명 union 멤버인 각 unionU에 대해,U의 (있다면) 첫 번째 변형 멤버가 암시적 수명(implicit-lifetime) 타입이라면,T의 기본 생성자는 그 멤버가 자신의 union의 활성 멤버가 아닌 경우 그 멤버의 수명을 시작시켜요.
암시적으로 정의된 기본 생성자는 빈 본문과 빈 이니셜라이저 목록을 가진 사용자 정의 생성자와 같은 효과를 가져요. 다시 말해 이 클래스의 기반들과 비정적 멤버들의 기본 생성자를 호출해요. 빈 사용자 제공(user-provided) 생성자를 가진 클래스 타입은, 값 초기화 중에 암시적으로 정의된 기본 생성자를 가진 타입과 다르게 취급될 수 있어요.
- (C++11부터) 이것이 constexpr 생성자(until C++23) / constexpr 함수(since C++23)의 요구사항을 만족하면, 생성된 생성자는
constexpr이 돼요. 사용자 정의 생성자가 있다 하더라도,default키워드를 쓰면 컴파일러가 (그렇지 않았다면 암시적으로 선언됐을) 기본 생성자를 강제로 자동 생성하게 할 수 있어요.
삭제된 기본 생성자
클래스 T의 암시적으로 선언되거나 명시적으로 defaulted된 기본 생성자는, 다음 조건 중 하나라도 만족하면 deleted로 정의돼요.
T가 기본 이니셜라이저(default initializer)가 없는 참조 타입의 비정적 데이터 멤버를 가진다.T가 비-union 클래스이고, (C++26부터) const 한정 타입(또는 그 다차원 배열)의, 비변형(non-variant) 비정적, non-const-default-constructible 데이터 멤버를 기본 멤버 이니셜라이저 없이 가진다. (C++26 이전에는 이 자리에 "union의 모든 변형 멤버가 const 한정 타입이거나 / 비-union 클래스에서 어떤 익명 union 멤버의 모든 멤버가 const 한정 타입"이라는 별도의 조건이 있었어요.)- 클래스 타입
M이 주어졌을 때,T가 타입M(또는 그 다차원 배열)의 잠재적 생성 부분 객체(potentially constructed subobject)obj를 가지고 있고, 다음 조건 중 하나를 만족한다.M의 소멸자가 기본 생성자에서 deleted이거나 접근 불가하고,obj가 비변형이거나 기본 멤버 이니셜라이저를 가진다.- 다음이 모두 만족한다.
obj가 기본 이니셜라이저를 가진 비정적 데이터 멤버가 아니다.- (C++26 이전)
obj가, 다른 비정적 데이터 멤버가 기본 이니셜라이저를 가진 union의 변형 멤버가 아니다. M의 기본 생성자를 찾는 오버로드 해석이 사용 가능한 후보를 만들지 못하거나, (C++26 이전)obj가 변형 멤버인 경우 비자명(non-trivial) 함수를 선택한다.
사용자 정의 생성자가 없고 암시적으로 선언된 기본 생성자가 trivial하지 않다면, delete 키워드로 컴파일러가 암시적으로 정의된 기본 생성자를 자동 생성하는 걸 막을 수 있어요.
자명한 기본 생성자 (Trivial default constructor)
클래스 T의 기본 생성자는 다음 조건을 모두 만족하면 trivial이에요.
- 생성자가 암시적으로 선언됐거나(until C++11) 사용자 제공(user-provided)이 아니다(since C++11).
T에 가상 멤버 함수가 없다.T에 가상 기반 클래스가 없다.- (C++11부터)
T에 기본 이니셜라이저를 가진 비정적 멤버가 없다. T의 모든 직접 기반이 trivial 기본 생성자를 가진다.- (C++26 이전) 클래스 타입(또는 그 배열)의 모든 비정적 멤버가 trivial 기본 생성자를 가진다. (C++26부터)
T가 union이거나, 클래스 타입(또는 그 배열)의 모든 비변형 비정적 멤버가 trivial 기본 생성자를 가진다.
trivial 기본 생성자는 아무 동작도 수행하지 않는 생성자예요. C 언어와 호환되는 모든 데이터 타입(POD 타입)은 trivially default-constructible이에요.
적격 기본 생성자 (Eligible default constructor)
- (C++11 이전) 기본 생성자가 사용자 선언됐거나, 암시적으로 선언되고 정의 가능하면 적격(eligible)이에요.
- (C++11~C++20) 기본 생성자가 deleted가 아니면 적격이에요.
- (C++20부터) 다음을 모두 만족하면 적격이에요.
- deleted가 아니다.
- (있다면) 연관 제약(associated constraints)을 만족한다.
- 연관 제약을 만족하는 기본 생성자 중 더 제약이 강한(more constrained) 것이 없다.
적격 기본 생성자의 trivial 여부는 클래스가 암시적 수명(implicit-lifetime) 타입인지, trivially copyable 타입인지를 결정해요.
참고 (Notes)
| 기능 검사 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_trivial_union |
202502L | (C++26) | union의 특별 멤버 함수에 대한 trivial성 요구 완화 |
예제 (Example)
언제 기본 생성자가 암시적으로 정의되고, 언제 deleted로 정의되는지를 코드로 확인해 볼게요. 각 구조체에 대한 주석을 주의 깊게 봐요.
struct A
{
int x;
A(int x = 1): x(x) {} // 사용자 정의 기본 생성자
};
struct B : A
{
// B::B()는 암시적으로 정의되며 A::A()를 호출
};
struct C
{
A a;
// C::C()는 암시적으로 정의되며 A::A()를 호출
};
struct D : A
{
D(int y) : A(y) {}
// D::D()는 다른 생성자가 있으므로 선언되지 않음
};
struct E : A
{
E(int y) : A(y) {}
E() = default; // 명시적으로 defaulted, A::A() 호출
};
struct F
{
int& ref; // 참조 멤버
const int c; // const 멤버
// F::F()는 deleted로 암시적으로 정의됨
};
// 사용자 선언 복사 생성자 (user-provided, deleted, defaulted 어느 쪽이든)
// 기본 생성자의 암시적 생성을 막는다
struct G
{
G(const G&) {}
// G::G()는 deleted로 암시적으로 정의됨
};
struct H
{
H(const H&) = delete;
// H::H()는 deleted로 암시적으로 정의됨
};
struct I
{
I(const I&) = default;
// I::I()는 deleted로 암시적으로 정의됨
};
int main()
{
A a;
B b;
C c;
// D d; // 컴파일 오류
E e;
// F f; // 컴파일 오류
// G g; // 컴파일 오류
// H h; // 컴파일 오류
// I i; // 컴파일 오류
}
여기서 확인할 핵심은 두 가지예요. 첫째, 기본 생성자를 하나도 선언하지 않으면 컴파일러가 기반·멤버의 기본 생성자를 호출하는 기본 생성자를 만들어 줘요(B, C). 둘째, 생성자를 하나라도 선언하면 기본 생성자는 자동 생성되지 않고, 대신 deleted로 정의돼요(D, G, H, I). 참조·const 멤버가 기본 이니셜라이저 없이 있으면 (F) 역시 deleted가 돼요.
결함 보고 (Defect reports)
다음의 동작을 바꾸는 결함 보고가 이전에 공표된 C++ 표준에 소급 적용됐어요.
| DR | 적용 대상 | 공표된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 1353 | C++11 | 암시적으로 선언된 기본 생성자가 deleted로 정의되는 조건이 다차원 배열 타입을 고려하지 않았음 | 이 타입들을 고려 |
| CWG 2084 | C++11 | 기본 멤버 이니셜라이저가 union의 defaulted 기본 생성자가 deleted인지에 영향을 주지 않았음 | defaulted 기본 생성자가 deleted되는 것을 막음 |
| CWG 2595 | C++20 | 다른 기본 생성자가 더 제약이 강하지만 연관 제약을 만족하지 못하면 기본 생성자가 적격이 아니었음 | 이 경우 적격일 수 있음 |
| CWG 2871 | C++98 | 사용자 선언 생성자 템플릿이 있어도 기본 생성자가 암시적으로 선언됐음 | 이 경우 암시적 선언 없음 |
더 알아보기 (Learn more)
- 생성자 (Constructor) — 객체 생성 시 호출되는 특별 멤버 함수 전반. (cppreference)
- 초기화 (Initialization) — 기본·값·복사·직접·리스트·참조 초기화 등 초기화의 각 형태가 언제, 어떻게 일어나는지. (cppreference)
- new 표현식 — 동적 메모리에서 객체를 생성할 때 호출되는 생성자 규칙. (cppreference)