복사 생성자(C++)

Copy constructor (C++)

C++ 프로그래밍 언어에서 카피 컨스트럭터는 기존 객체의 카피로서 새로운 객체를 작성하기 위한 특별한 컨스트럭터입니다.Copy Constructor는 C++에서 객체를 복제하는 표준 방법으로 C++ 고유의 차이점이 있습니다.

이러한 생성자의 첫 번째 인수는 생성 중인 것과 동일한 유형의 객체(상수 또는 비상수)에 대한 참조이며, 그 뒤에 모든 유형의 파라미터(모두 기본값이 있음)가 뒤따를 수 있습니다.

보통 컴파일러는 각 클래스(암묵적 복사 생성자로 알려져 있음)에 대해 자동으로 복사 생성자를 생성하지만 특별한 경우 프로그래머는 사용자 정의 복사 생성자로 알려진 복사 생성자를 생성합니다.이 경우 컴파일러는 작성하지 않습니다.따라서 사용자 또는 시스템에 의해 정의되는 복사 생성자가 항상 1개 있습니다.

일반적으로 사용자 정의 복사 생성자는 객체가 포인터 또는 공유 불가능한 참조(를 들어 파일대한 참조)를 소유할 때 필요합니다. 이 경우 소멸자 및 할당 연산자도 작성해야 합니다(규칙 3 참조).

정의.

오브젝트 복사는 복사 생성자 및 할당 연산자를 사용하여 이루어집니다.복사 생성자는 첫 번째 매개 변수로 자신의 클래스 유형에 대한 참조(항상 또는 휘발성일 수 있음)를 가집니다.인수를 더 많이 지정할 수 있지만 나머지 인수에는 [1]기본값이 관련되어 있어야 합니다.다음은 클래스에 유효한 복사 생성자입니다.X:

X(컨스턴트 X& copy_from_me); X(X& copy_from_me); X(휘발성의 X& copy_from_me); X(컨스턴트 휘발성의 X& copy_from_me); X(X& copy_from_me, 인트 = 0); X(컨스턴트 X& copy_from_me, 더블 = 1.0, 인트 = 42); ... 

다른 하나를 사용해야 할 정당한 이유가 없는 한 첫 번째 것을 사용해야 합니다.첫 번째와 두 번째의 차이점 중 하나는 임시 파일을 첫 번째와 함께 복사할 수 있다는 것입니다.예를 들어 다음과 같습니다.

X a = X();     // 지정 X(const X& copy_from_me)는 유효하지만 지정 X(X& copy_from_me)는 유효하지 않습니다.                // 두 번째는 일정하지 않은 X&를 원하기 때문에                // 를 작성하기 위해 컴파일러는 먼저 기본 생성자를 호출하여 임시 생성자를 만듭니다.                // X의 경우 복사 생성자를 사용하여 해당 임시 복사본으로 초기화합니다.                 // 프로그램 실행 중에 생성된 임시 객체는 항상 const 유형입니다.따라서 const 키워드가 필요합니다.                // 컴파일러에 따라서는 두 버전 모두 실제로 동작하지만 이 동작에 의존해서는 안 됩니다.                // 표준이 아니기 때문에. 

동일한 차이가 직접 복사하려고 할 때 적용됩니다.const오브젝트:

컨스턴트 X a; X b = a;       // 지정 X(const X& copy_from_me)는 유효하지만 지정 X(X& copy_from_me)는 유효하지 않습니다.                // 두 번째는 일정하지 않은 X&를 원하기 때문에 

X&복사된 개체를 수정해야 할 경우 복사 생성자의 형식을 사용합니다.이는 매우 드물지만 표준 라이브러리에서 사용되는 것을 볼 수 있습니다.std::auto_ptr. 참조를 제공해야 합니다.

X a; X b = a;       // 정의된 복사 생성자가 있는 경우 유효합니다.                // 참조가 전달되고 있기 때문에. 

다음은 다음과 같은 이유로 인해 잘못된 복사 생성자입니다.copy_from_me참조로서 전달되지 않습니다(&) :

X(X copy_from_me); X(컨스턴트 X copy_from_me); 

왜냐하면 이러한 컨스트럭터에 대한 호출에는 복사본도 필요하기 때문에 무한 재귀 호출이 발생합니다.

다음과 같은 경우 복사 컨스트럭터에 대한 호출이 발생할 수 있습니다.

  1. 개체 값이 반환되는 경우
  2. 객체가 인수로 값에 의해 (함수에) 전달되는 경우
  3. 오브젝트가 던져질 때
  4. 객체가 값으로 포착된 경우
  5. 오브젝트가 괄호로 둘러싸인 이니셜라이저 리스트에 배치되는 경우

이러한 경우는, 통칭해 카피 초기화라고 불리며,[2] 다음과 같습니다. T x = a;

단, C++ 표준에 의해 컴파일러가 복사를 최적화할 수 있기 때문에 이러한 경우 복사 컨스트럭터가 호출되는 것은 보증되지 않습니다.예를 들어 반환값 최적화(RVO라고도 함)가 있습니다.

작동

오브젝트에는 다음 두 가지 방법 중 하나를 사용하여 값을 할당할 수 있습니다.

  • 식에 명시적 할당
  • 초기화

식에 명시적 할당

물건 a; 물건 b; a = b;       // 오브젝트::operator=(const Object&)로 변환되므로 a.param=(b)가 호출됩니다.              // (복사 생성자가 아닌 단순 복사가 필요합니다! 

초기화

오브젝트는 다음 중 하나의 방법으로 초기화할 수 있습니다.

a. 신고에 의한

물건 b = a; // 개체로 변환::오브젝트(const Object &) (복사 생성자를 호출) 

b. 함수 인수를 통해

유형 기능.(물건 a); 

c. 스루 함수 반환값

물건 a = 기능.(); 

복사 생성자는 초기화에만 사용되며 할당 연산자가 대신 사용되는 할당에는 적용되지 않습니다.

클래스의 암묵적 복사 생성자는 기본 복사 생성자를 호출하고 해당 유형의 적절한 방법으로 해당 구성원을 복사합니다.클래스 유형인 경우 복사 생성자가 호출됩니다.스칼라 타입의 경우는, 짜넣기 할당 연산자가 사용됩니다.마지막으로 배열일 경우 각 요소를 [3]그 유형에 맞게 복사한다.

프로그래머는 사용자 정의 복사 생성자를 사용함으로써 오브젝트가 복사되었을 때 실행되는 동작을 정의할 수 있다.

이러한 예는 복사 생성자의 작동 방식과 경우에 따라 복사 생성자가 필요한 이유를 보여 줍니다.

암묵적 복사 생성자

다음 예를 생각해 보겠습니다.

#실패하다 <iostream>  학급 사람인 {  일반의:   명시적 사람인(인트 나이) : 나이(나이) {}    인트 나이; };  인트 주된() {   사람인 티미(10);   사람인 샐리(15);    사람인 timmy_clone = 티미;   표준::외치다 << > 티미.나이 << > " " << > 샐리.나이 << > " " << > timmy_clone.나이             << > 표준::;   티미.나이 = 23;   표준::외치다 << > 티미.나이 << > " " << > 샐리.나이 << > " " << > timmy_clone.나이             << > 표준::; } 

산출량

10 15 10 23 15 10

역시나timmy새 객체에 복사되었습니다.timmy_clone.하는 동안에timmy나이도 바뀌었고timmy_clone나이는 변하지 않았어요.왜냐하면 그들은 완전히 다른 물체이기 때문입니다.

컴파일러는 우리를 위해 복사 컨스트럭터를 생성했고 다음과 같이 기록될 수 있습니다.

사람인(컨스턴트 사람인& 다른.)      : 나이(다른..나이)  // 시대의 복사 생성자를 호출합니다. { } 

그렇다면 사용자 정의 복사 생성자가 실제로 필요한 때는 언제일까요?다음 섹션에서는 이 질문에 대해 살펴보겠습니다.

사용자 정의 복사 생성자

다음과 같은 매우 단순한 동적 어레이 클래스를 고려합니다.

#실패하다 <iostream>  학급 어레이 {  일반의:   명시적 어레이(인트 크기) : 크기(크기), 데이터.(신규 인트[크기]) {}    ~어레이() {     한다면 (데이터. != 특수) {       삭제하다[] 데이터.;     }   }    인트 크기;   인트* 데이터.; };  인트 주된() {   어레이 첫번째(20);   첫번째.데이터.[0] = 25;    {     어레이 알았다. = 첫번째;     표준::외치다 << > 첫번째.데이터.[0] << > " " << > 알았다..데이터.[0] << > 표준::;   }  // (1)    첫번째.데이터.[0] = 10;  // (2) } 

산출량

25 세그멘테이션 장애

copy constructor를 지정하지 않았기 때문에 컴파일러가 copy constructor를 생성했습니다.생성된 생성자는 다음과 같습니다.

어레이(컨스턴트 어레이& 다른.)   : 크기(다른..크기), 데이터.(다른..데이터.) {} 

이 생성자의 문제는 데이터 포인터의 얕은 복사를 수행하는 것입니다.원래 데이터 멤버의 주소만 복사됩니다. 즉, 두 멤버 모두 동일한 메모리 청크로의 포인터를 공유하지만 이는 우리가 원하는 것이 아닙니다.프로그램이 행 (1)에 도달하면 복사 소멸자가 호출됩니다(스택의 오브젝트는 스코프가 종료되면 자동으로 파기되기 때문입니다).Array의 소멸자는 원본의 데이터 배열을 삭제하기 때문에 복사본의 데이터를 삭제할 때 동일한 포인터를 공유하기 때문에 먼저 해당 데이터도 삭제했습니다.(2)이 무효 데이터에 액세스 해, 기입합니다.로 인해 분할 결함이 발생합니다.

카피를 실행하는 독자적인 카피 컨스트럭터를 작성하면, 이 문제는 해소됩니다.

// 표준:: 복사 #실패하다 <blocks>  어레이(컨스턴트 어레이& 다른.)     : 크기(다른..크기), 데이터.(신규 인트[다른..크기]) {   표준::알았다.(다른..데이터., 다른..데이터. + 다른..크기, 데이터.);  } 

여기에서는 새로운 int 배열을 생성하여 내용을 복사하고 있습니다.이제 다른 파괴자는 첫 번째 데이터가 아닌 자신의 데이터만 삭제합니다.라인(2)은 더 이상 분할 결함을 생성하지 않습니다.

즉시 상세 복사를 수행하는 대신 사용할 수 있는 최적화 전략이 몇 가지 있습니다.이를 통해 여러 개체 간에 동일한 데이터를 안전하게 공유할 수 있으므로 공간을 절약할 수 있습니다.Copy-on-Write 전략은 데이터를 쓸 때만 데이터의 복사본을 만듭니다.참조 카운트는 데이터를 참조하는 개체 수를 유지하며, 이 카운트가 0에 도달한 경우에만 삭제됩니다(예:boost::shared_ptr).

생성자 및 템플릿 복사

예상과 달리 템플릿 복사본 생성자는 사용자 정의 복사본 생성자가 아닙니다.따라서 다음과 같은 것만으로는 충분하지 않습니다.

템플릿 <>타이프네임 A> 어레이::어레이(A 컨스턴트& 다른.)     : 크기(다른..크기()), 데이터.(신규 인트[다른..크기()]) {   표준::알았다.(다른..시작한다.(), 다른..끝.(), 데이터.); } 

(유형은A수 있습니다.Array어레이에서 어레이를 구축하기 위해 사용자 정의 비템플릿 복사 컨스트럭터도 제공해야 합니다.

비트 복사 생성자

C++에는 "bitwise copy constructor"와 같은 것은 없습니다.단, 기본 생성 복사 생성자는 멤버에 대해 복사 생성자를 호출하여 복사본을 생성하며, 원시 포인터 멤버의 경우 원시 포인터(즉, 딥 복사본이 아님)를 복사합니다.

논리 복사 생성자

논리 복사 생성자에서는 값을 [4]복사하는 것과 함께 포인터에 대해 새로운 동적 멤버 변수가 작성되는 것을 알 수 있습니다.

논리 복사 생성자는 동적 구조뿐만 아니라 구조의 실제 복사본을 만듭니다.논리 복사 생성자는 주로 복사되는 개체 내에 포인터나 복잡한 개체가 있을 때 나타납니다.

명시적 복사 생성자

명시적 복사 생성자는 explicit 키워드를 사용하여 명시적으로 선언되는 생성자입니다.예를 들어 다음과 같습니다.

명시적 X(컨스턴트 X& copy_from_me); 

함수 호출 시 또는 복사 초기화 구문을 사용하여 객체의 복사를 방지하기 위해 사용됩니다.

「 」를 참조해 주세요.

레퍼런스

  1. ^ INCITS ISO IEC 14882-2003 12.8.2. [1] 2007년 6월 8일 Wayback Machine 아카이브 완료
  2. ^ ISO/IEC(2003)ISO/IEC 14882:2003(E): 프로그래밍 언어 - C++ © 8.5 이니셜라이저 [dcl.init] para. 12
  3. ^ INCITS ISO IEC 14882-2003 12.8.8. [2] 2007년 6월 8일 Wayback Machine 아카이브 완료
  4. ^ 컴퓨터 과학 Behrouz A의 C++를 사용한 구조화된 접근법.포우잔과 리처드 F.Gilberg, 그림 10-9, 507페이지