복사 생성자

복사 생성자 (Copy constructor)

같은 타입의 객체로부터 새 객체를 만들어 초기화하고 싶을 때 쓰는 게 복사 생성자예요. 같은 클래스 타입의 인자로 호출될 수 있고, 인자의 내용을 복사하되 인자를 변경하지 않는 생성자를 말하죠.

출처: cppreference

본문

복사 생성자는 같은 클래스 타입의 인자로 호출될 수 있고 인자의 내용을 복사하되 인자를 변경하지 않는 생성자예요.

구문 (Syntax)

구문 번호
class-name(parameter-list); (1)
class-name(parameter-list) function-body (2)
class-name(single-parameter-list) = default; (3) (since C++11)
class-name(parameter-list) = delete; (4) (since C++11)
class-name::class-name(parameter-list) function-body (5)
class-name::class-name(single-parameter-list) = default; (6) (since C++11)
항목 - 설명
class-name - 복사 생성자를 선언하는 클래스
parameter-list - 다음 조건을 모두 만족하는 비어 있지 않은 매개변수 목록: 클래스 타입을 T라 할 때 첫 번째 매개변수는 T&, const T&, volatile T&, const volatile T& 타입 중 하나이고, 다른 매개변수가 없거나 모든 다른 매개변수가 기본 인자를 가짐
single-parameter-list - T&, const T&, volatile T&, const volatile T& 타입 중 하나이고 기본 인자를 갖지 않는 단 하나의 매개변수 목록
function-body - 복사 생성자의 함수 본문

설명 (Explanation)

  1. 클래스 정의 안에서의 복사 생성자 선언. 2-4) 클래스 정의 안에서의 복사 생성자 정의.
  2. 복사 생성자가 명시적으로 defaulted됨.
  3. 복사 생성자가 deleted됨. 5,6) 클래스 정의 밖에서의 복사 생성자 정의 (클래스는 (1)의 선언을 포함해야 함).
  4. 복사 생성자가 명시적으로 defaulted됨.
struct X
{
    X(X& other); // copy constructor
//  X(X other);  // Error: incorrect parameter type
};

union Y
{
    Y(Y& other, int num = 1); // copy constructor with multiple parameters
//  Y(Y& other, int num);     // Error: `num` has no default argument
};

복사 생성자는 (오버로드 해석이 더 나은 일치를 선택하거나 호출이 생략(elided)되지 않는 한) 같은 타입의 다른 객체로부터 객체가 초기화될 때(직접 초기화나 복사 초기화로) 호출돼요. 여기에는 다음이 포함돼요:

  • 초기화: bT 타입일 때 T a = b; 또는 T a(b);
  • 함수 인자 전달: aT 타입이고 fvoid f(T t)일 때 f(a);
  • 함수 반환: 이동 생성자가 없는 T f() 같은 함수 안에서 aT 타입일 때 return a;

암시적으로 선언된 복사 생성자 (Implicitly-declared copy constructor)

클래스 타입에 사용자 정의 복사 생성자가 없으면, 컴파일러는 항상 클래스의 비-explicit inline public 멤버로 복사 생성자를 선언해요. 이 암시적으로 선언된 복사 생성자는 다음이 모두 참이면 T::T(const T&) 형태를 가져요:

  • T의 각 직접 및 가상 기반 Bconst B& 또는 const volatile B& 타입 매개변수를 갖는 복사 생성자를 가짐
  • T의 클래스 타입 또는 클래스 타입 배열인 각 비정적 데이터 멤버 Mconst M& 또는 const volatile M& 타입 매개변수를 갖는 복사 생성자를 가짐

그 외에는 암시적으로 선언된 복사 생성자가 T::T(T&)예요.

이 규칙 때문에 암시적으로 선언된 복사 생성자는 volatile lvalue 인자에 바인딩할 수 없어요.

클래스는 여러 복사 생성자를 가질 수 있어요, 예를 들어 T::T(const T&)T::T(T&)를 모두 가질 수 있죠.

사용자 정의 복사 생성자가 있어도 default 키워드로 암시적 복사 생성자 선언을 강제할 수 있어요.(since C++11)

암시적으로 선언된 (또는 첫 번째 선언에서 defaulted된) 복사 생성자는 dynamic exception specification(until C++17)noexcept specification(since C++17)에 설명된 예외 명세를 가져요.

암시적으로 정의된 복사 생성자 (Implicitly-defined copy constructor)

암시적으로 선언된 복사 생성자가 deleted가 아니면, odr-used되거나 상수 평가(since C++11)에 필요할 때 컴파일러가 정의해요(즉 함수 본문이 생성되고 컴파일됨). 유니온 타입의 경우 암시적으로 정의된 복사 생성자는 객체 표현을 복사해요(std::memmove처럼). 비유니온 클래스 타입의 경우, 이 생성자는 객체의 직접 기반 부분 객체와 멤버 부분 객체를 초기화 순서대로 직접 초기화(direct initialization)를 사용해 전체 멤버별 복사(full member-wise copy)를 수행해요. 참조 타입의 각 비정적 데이터 멤버에 대해 복사 생성자는 원본 참조가 바인딩된 것과 같은 객체 또는 함수에 참조를 바인딩해요.

참고 (since)
이 생성자가 constexpr 생성자(until C++23)constexpr 함수(since C++23)의 요구 사항을 충족하면 생성된 복사 생성자는 constexpr이다. T에 사용자 정의 소멸자나 사용자 정의 복사 대입 연산자가 있으면 암시적으로 정의된 복사 생성자의 생성은 deprecated된다. (since C++11)

삭제된 복사 생성자 (Deleted copy constructor)

클래스 T에 대한 암시적으로 선언되거나 명시적으로 defaulted된(since C++11) 복사 생성자는 다음 조건 중 하나라도 만족하면 undefined(until C++11)defined as deleted(since C++11)돼요:

참고 (since)
T가 rvalue 참조 타입의 비정적 데이터 멤버를 갖는 경우 (since C++11)
  • T가 클래스 타입 M(또는 그 다차원 배열)의 잠재적으로 구성되는 부분 객체를 갖는데
    • M이 복사 생성자로부터 deleted 또는(since C++11) 접근 불가능한 소멸자를 갖거나,
    • M의 복사 생성자를 찾기 위한 오버로드 해석이
      • 사용 가능한 후보를 만들지 못하거나,
      • 부분 객체가 variant 멤버인 경우 비trivial 함수를 선택하는 경우
참고 (since)
T가 이동 생성자 또는 이동 대입 연산자를 선언하면 클래스 T의 암시적으로 선언된 복사 생성자는 deleted로 정의된다. (since C++11)

trivial 복사 생성자 (Trivial copy constructor)

클래스 T의 복사 생성자는 다음이 모두 참이면 trivial이에요:

  • 사용자 제공이 아님 (즉 암시적으로 정의되거나 defaulted됨)
  • T에 가상 멤버 함수가 없음
  • T에 가상 기반 클래스가 없음
  • T의 각 직접 기반에 대해 선택된 복사 생성자가 trivial임
  • T의 각 비정적 클래스 타입(또는 클래스 타입 배열) 멤버에 대해 선택된 복사 생성자가 trivial임

비유니온 클래스에 대한 trivial 복사 생성자는 인자의 모든 스칼라 부분 객체(재귀적으로 부분 객체의 부분 객체 등을 포함)를 효과적으로 복사하고 다른 동작은 수행하지 않아요. 그러나 패딩 바이트는 복사될 필요가 없고, 복사된 부분 객체의 객체 표현조차 값이 동일하기만 하면 같을 필요는 없어요.

TriviallyCopyable 객체는 객체 표현을 수동으로 복사해 복사할 수 있어요, 예를 들어 std::memmove로요. C 언어와 호환되는 모든 데이터 타입(POD 타입)은 trivially copyable이에요.

적격 복사 생성자 (Eligible copy constructor)

정의 (since)
복사 생성자는 사용자 선언이거나 암시적으로 선언되고 정의 가능한 경우에 적격이다. (until C++11)
복사 생성자는 deleted가 아니면 적격이다. (since C++11)(until C++20)
복사 생성자는 다음 조건이 모두 충족되면 적격이다: deleted가 아님. 연관 제약(있다면)이 만족됨. 연관 제약이 만족되는 복사 생성자 중 더 제한적인 것이 없음. (since C++20)

적격 복사 생성자의 trivial 여부는 클래스가 implicit-lifetime 타입인지, trivially copyable 타입인지 결정해요.

참고 (Notes)

많은 상황에서 복사 생성자는 관측 가능한 부수 효과를 만들더라도 최적화로 제거될 수 있어요. 복사 생략을 참고하세요.

예제 (Example)

struct A
{
    int n;
    A(int n = 1) : n(n) {}
    A(const A& a) : n(a.n) {} // user-defined copy constructor
};

struct B : A
{
    // implicit default constructor B::B()
    // implicit copy constructor B::B(const B&)
};

struct C : B
{
    C() : B() {}
private:
    C(const C&); // non-copyable, C++98 style
};

int main()
{
    A a1(7);
    A a2(a1); // calls the copy constructor
    
    B b;
    B b2 = b;
    A a3 = b; // conversion to A& and copy constructor
    
    volatile A va(10);
    // A a4 = va; // compile error
    
    C c;
    // C c2 = c; // compile error
}

A a2(a1);은 명시적으로 사용자 정의 복사 생성자를 호출해요. B b2 = b;는 암시적 복사 생성자를 쓰죠. C는 복사 생성자가 private이라 복사가 불가능하니 C c2 = c;는 컴파일 오류가 나요.

더 알아보기 (Learn more)

  • 변환 생성자 — 암시적 변환을 일으키는 생성자.
  • 복사 대입 — operator=를 통한 복사 대입 연산자.
  • 복사 생략 — 복사·이동 생성이 생략될 수 있는 상황.
  • 기본 생성자, 소멸자, explicit.
  • 각 초기화 방식.
  • 이동 대입, 이동 생성자.