18. [C++]복사 생성자(Copy Constructor)
2024. 4. 15. 10:43ㆍ[C++]/C++ 언어 기초
C++ 스타일의 초기화
기존 변수, 참조자 선언 및 초기화 방법
int num = 20;
int &ref = num;
C++에서의 변수, 참조자 선언 및 초기화
int num(20);
int &ref(num);
- 위 두 가지 초기화 방식은 결과적으로 동일하다.
- C++에서는 위 두 가지 방식의 초기화를 지원한다.
#include "stdafx.h"
#include <iostream>
using namespace std;
using namespace System;
class SoSImple{
private:
int num1;
int num2;
public:
SoSImple(int n1, int n2) : num1(n1), num2(n2){}
void ShowSimpleData(){
cout<<num1<<endl;
cout<<num2<<endl;
}
};
int main(void)
{
SoSImple sim1(15, 20);
SoSImple sim2 = sim1; //sim2에 sim1을 복사
sim2.ShowSimpleData();
return 0;
}
15
20
- sim2 객체를 새로 생성해서, 객체 sim1과 sim2간의 멤버 대 멤버 복사가 일어난다고 예상할 수 있다.
int num1 = num2;
int num1(num2);
- 이 문장이 동일하듯이
SoSimple sim2 = sim1;
Sosimple sim2(sim1);
- 이 두 문장도 동일한 의미로 해석이 된다.
SoSimple sim2(sim1)
문장의 의미는 다음과 같다.
- SoSimple형 객체를 생성해라
- 객체의 이름은 sim2로 정한다.
- sim1을 인자로 받을 수 있는 생성자의 호출을 통해서 객체생성을 완료한다.
SoSimple(SoSimple ©){
...
}
- 위의 객체 생성문에서 호출하고자 하는 생성자는 SoSimple 객체를 인자로 받을 수 있는 생성자이다.
1. SoSimple sim2 = sim2;
2. SoSimple sim2(sim1);
- 1번 문장도 실은 2번의 형태로 묵시적 변환이 되어서 객체가 생성되는 것이다.
ClassInit.cpp
#include <iostream>
using namespace std;
class SoSimple
{
private:
int num1;
int num2;
public:
SoSimple(int n1, int n2)
: num1(n1), num2(n2)
{
// empty
}
SoSimple(SoSimple ©)
: num1(copy.num1), num2(copy.num2)
{
cout<<"Called SoSimple(SoSimple ©)"<<endl;
}
void ShowSimpleData()
{
cout<<num1<<endl;
cout<<num2<<endl;
}
};
int main(void)
{
SoSimple sim1(15, 30);
cout<<"생성 및 초기화 직전"<<endl;
SoSimple sim2=sim1; //Sosimple sim2(sim1)으로 묵시적 변환
cout<<"생성 및 초기화 직후"<<endl;
sim2.ShowSimpleData();
return 0;
}
생성 및 초기화 직전
Called SoSimple(SoSimple ©)
생성 및 초기화 직후
15
30
- 위 예제에서 SoSimple sim2 = sim1; 부분은 묵시적으로 변환된다.
SoSimple(SoSimple ©)
: num1(copy.num1), num2(copy.num2)
{
cout<<"Called SoSimple(SoSimple ©)"<<endl;
}
- 예제에서 이러한 생성자를 가리켜 별도로 "복사 생성자"라고 한다.
- 이 생성자는 호출되는 시점이 다른 일반 생성자와 차이가 있다.
SoSimple(const SoSimple ©)
: num1(copy.num1), num2(copy.num2)
{
cout<<"Called SoSimple(SoSimple ©)"<<endl;
}
- 또한 멤버 대 멤버의 복사에 사용되는 원본을 변경시키는 것은 복사의 개념을 무너뜨리는 행위가 되니, 키워드 const를 삽입해 이러한 실수를 막아 놓는 것이 좋다.
자동으로 삽입이 되는 디폴트 복사 생성자
- 복사 생성자를 정의하지 않으면, 멤버 대 멤버의 복사를 진행하는 디폴트 복사 생성자가 자동으로 삽입된다.
- 생성자가 존재하더라도, 복사 생성자가 정의되어 있지 않으면 디폴트 복사 생성자라는 것이 삽입되어 멤버 대 멤버의 복사를 진행한다.
- 따라서 위의 코드는 완전히 동일하다. 디폴트 복사 생성자가 자동으로 삽입되기 때문이다.
변환에 의한 초기화! 키워드 explicit으로 막을 수 있다
SoSimple sim2 = sim1;
- 위 코드는 묵시적 변환이 일어나서 복사 생성자가 호출된다고 설명했다.
SoSimple sim2(sim1);
- 이는 결국, 복사 생성자가 묵시적으로 호출된 것이다.
- explicit 키워드는, 복사 생성자의 묵시적 호출을 허용하지 않게 해주는 키워드이다.
explicit SoSimple(const SoSimple ©) : num1(copy.num1), num2(copy.num2){
//empty!
}
- 이렇게 explicit 키워드를 사용하게 되면 묵시적 변환이 발생하지 않아 대입 연산자를 이용한 객체의 생성 및 초기화가 불가능하다.
이러한 문장의 묵시적 변환은 복사 생성자에서만 일어나는 게 아니다.
전달인자가 하나인 생성자가 있다면, 이 역시 묵시적 변환이 발생한다.
class AAA{
private:
int num;
public:
AAA(int n) : num(n){}
-----
};
- 이 코드는 AAA obj = 3; //AAA obj(3)으로 변환된다.
- 이 경우에도 마찬가지로 explicit 키워드를 사용해주면 AAA obj(3)으로만 생성이 가능해진다.
반응형
'[C++] > C++ 언어 기초' 카테고리의 다른 글
20. [C++] 복사 생성자의 호출시점 (0) | 2024.04.15 |
---|---|
19. [C++] 깊은 복사와 얕은 복사 (0) | 2024.04.15 |
04-05 은행계좌문제 2단계 (0) | 2024.04.15 |
16. [C++] 생성자(Constructor)와 소멸자(Destructor) (0) | 2024.04.12 |
15. [C++] 객체지향 프로그래밍의 이해, 정보은닉, 캡슐화 (0) | 2024.04.12 |