반응형
복사 생성자란?
→ 한 객체의 내용을 다른 객체로 복사하여 생성된 생성자
- 일반적으로 생성된 생성자와 같은데 그 내용이 다른 객체를 복사하여 생성된 거승로 이해
복사 생성자읙 특징
- 자신과 같은 타입의 객체를 인자로 받는다.
- 복사 생성자가 정의 되어 있지 않다면, 디폴트 복사 생성자가 생성됨.
디폴트 복사 생성자?
→일반 생성자에서 디폴트 생성자의 특징과 똑같다면 보면 되는데 클래스 내에 복사 상생자를 정의하지 않더라도 객체 생성 시 컴파일러가 자동으로 복사 생성자를 생성한다.
복사 생성자의 가장 기본적인 코드
class Test {
public:
Test(const Test& obj) { // copy constructor
...
}
}
명심해야될 부분
- 앞으로 복사 생성자를 정의해야 할 상황 → 매개 변수를 선언할 때 항상 위와 같은 형식으로 구현
- 매개 변수 → const Test& obj
반환형을 레퍼런스 타입으로 선언?
- 객체의 경우, 일반적으로 차지하는 메모리가 크기 때문에 매개 변수가 다음과 같이 선언되어 있다면 → Test(const Test obj)
- 복사할 객체를 매개 변수(obj)에 전부 복사하고 다시 매개 변수를 새로운 객체에 복사하는 작업을 수행하기 때문에 상당히 비효울적, 따라서 반환형을 레퍼런스 타입으로 정의함으로써 기존 객체를 매개 변수 obj에 복사하는 과정 없이 직접 이용하여 새로운 객체에 복사하는 것.
매개변수를 const 타입으로 선언 이유?
- const 타입 → 선언된 변수는 이후에 객체의 값을 변경할 수가 없고 읽기만 가능.
- 함수의 매개변수를 const 타입으로 선언 → 그 매개변수가 함수 내에서 변경되지 않고 읽기만 가능하도록 한 것 의미.
복사 생성자에 대입해보자
- 복사 생성자의 인자로 들어오는 “복사할 객체”는 생성자 내에서 값을 변경할 일이 전혀 없다. 따라서 const 타입으로 선언함으로써 생성자 내에서 읽기의 기능만을 수행하도록 하는 것.
디폴트 복사 생성자를 사용할 때 주의할 점
→ 디폴트 복사 생성자는 “얕은 복사(shallow copy)”를 수행.
- 디폴트 복사 생성자에 의해 초기화된 객체는 복사한 객체가 reference와 value 모두 똑같이 가지게 됨.
- value 할 때는 문제가없지만 reference를 복사할 때 → 특히, 동적 할당된 변수를 복사할 때 문제 생김.
이를 해결하기 위해 필드에 동적 할당을 받아 초기화 되는 변수가 있다면 디폴트 복사 생성자를 이용하지 않고 사용자가 복사 생성자를 직접 정의하여 깊은 복사(deep copy)가 일어나도록 해야함.
코드를 보면서 이해
#include <iostream>
using namespace std;
class Test {
int n;
int *m;
public:
Test(int x, int *y) { // Default Constructor
n = x;
m = new int[5];
copy(y, y+5, m);
}
int getN() { return n; }
void printM() {
for (int i = 0; i < 5; i++) cout << m[i] << " ";
cout << endl;
}
void setM(int idx, int t) {
m[idx] = t;
}
};
int main() {
int a = 150;
int arr[5] = {0, 1, 2, 3, 4};
Test t1(300, arr); // Default Constructor 호출
Test t2(t1); // Default Copy Constructor 호출
cout << "<Compare value of n>" << endl;
cout << t1.getN() << endl;
cout << t2.getN() << endl;
cout << "<Compare value of m>" << endl;
t1.printM();
t2.printM();
t2.setM(2, 80); // t2에서 배열 m의 2번 index의 value 변경
cout << "<After the change of m's value, Compare value of m>" << endl;
t1.printM();
t2.printM();
}
- 선언된 n int형으로 선언된 변수로 복사가 일어날 때, n에 저장된 value(t1에서 n의 value)를 복사하게 된다.
- 변수 m은 int형 포인터로 선언되어 생성자에서 동적 할당을 받아 초기화 된다.
- 디폴트 복사 생성자를 통해 복사를 하게 되면 변수 m즉, 1차원 배열 m이 가진 값들이 복사하는 것이 아니라 “배열 m이 저장된 주소”를 복사(얕은 복사)
- 현재 복사를 하는 복사 생성자가가 선언 되어 있지 않음 → 디폴트 복사
결과
300
300
<Compare value of m>
0 1 2 3 4
0 1 2 3 4
<After the change of m's value, Compare value of m>
0 1 80 3 4
0 1 80 3 4
- main 함수를 보면 t2에서 배열 m의 2번 index의 값만 80으로 변경 → t1의 값 또한 같이 변경됨
- 복사 생성자를 직접 정의하여 깊은 복사가 일어나도록 해결하자
#include <iostream>
using namespace std;
class Test {
int n;
int *m;
public:
Test(int x, int *y) { // Default Constructor
n = x;
m = new int[5];
copy(y, y+5, m);
}
Test(const Test& obj) { // Copy Constructor
n = obj.n;
m = new int[5]; // 깊은 복사
copy(obj.m, obj.m + 5, m);
}
int getN() { return n; }
void printM() {
for (int i = 0; i < 5; i++) cout << m[i] << " ";
cout << '\\n';
}
void setM(int idx, int t) {
m[idx] = t;
}
};
int main() {
int a = 150;
int arr[5] = {0, 1, 2, 3, 4};
Test t1(300, arr); // Default Constructor 호출
Test t2(t1); // Copy Constructor 호출
cout << "<Compare value of n>" << endl;
cout << t1.getN() << endl;
cout << t2.getN() << endl;
cout << "<Compare value of m>" << endl;
t1.printM();
t2.printM();
t2.setM(2, 80);
cout << "<After the change of m's value, Compare value of m>" << endl;
t1.printM();
t2.printM();
}
<Compare value of n>
300
300
<Compare value of m>
0 1 2 3 4
0 1 2 3 4
<After the change of m's value, Compare value of m>
0 1 2 3 4
0 1 80 3 4
→ 이 블로그에서 정리를 너무 잘해주셔서 이걸로 공부함.
반응형
'C++ > C++ 복사 생성자, 복사 할당 연산자(Canonical form)' 카테고리의 다른 글
깊은 복사 (0) | 2024.01.24 |
---|---|
복사 생성자와 복사 할당 연산자(Canonical form) (0) | 2023.12.01 |
복사 할당 연산자 (0) | 2023.12.01 |