안녕하세요 판타지코딩입니다!
저번 시간에는 C++의 얕은 복사에 대해서 공부해 보았습니다. 오늘은 얕은 복사와 디폴트 복사 생성자에 대해서 정리하고, 복사 생성자와 깊은 복사에 대해서 공부해 보겠습니다.
디폴트 복사 생성자
객체를 생성할 때 이전에 생성한 객체를 인자로 받아 멤버 변수를 복사할 때 자동으로 호출되는 것이 바로 '디폴트 복사 생성자'입니다. 개발자가 복사 생성자를 직접 정의하지 않으면 자동으로 호출되는 생성자입니다.
student s1 = s1("Education");
student s2(s1);
디폴트 복사 생성자 예제입니다. student 클래스가 정의되어 있다고 가정하고, 멤버 변수로 char *major가 있다고 가정하겠습니다. 포인터형 변수는 동적 할당을 받기 때문에 처음 객체 생성시 "Education"의 길이 만큼 메모리가 할당됩니다.
student(const char* majors)
{
major = new char[strlen(majors)+1];
strcpy(major, majors);
}
일반 생성자입니다. majors를 인자로 받아서 길이+1 만큼 동적 할당을 하고, major에 majors의 값을 복사하여 붙여넣습니다. 이런 과정을 통해서 멤버 변수 major가 초기화 됩니다.
그런데 위에서 s2객체를 만들 때 처럼 student s2(s1);을 실행하면 객체에서 객체로 복사를 하는 것이기 때문에 일반 생성자가 호출되는 것이 아니라 '복사 생성자'가 호출됩니다. 그런데 복사 생성자를 정의하지 않았다면 '디폴트 복사 생성자'가 호출됩니다. 그러므로 일반 변수는 그대로 복사가되고, 동적 할당을 하는 변수는 '주소값'이 복사됩니다. 이것을 바로 '얕은 복사'라고 합니다. s1 객체와 s2 객체가 major의 같은 주소값을 공유하기 때문입니다.
얕은 복사
s1과 s2가 major의 주소값을 공유하고 있는지 확인하기 위해 s2의 major의 값을 변경해 보겠습니다.
s2.setMajor("Language");
cout << s1.major << endl;
cout << s2.major << endl;
setMajor 함수로 s2의 major 값을 "Language"로 변경했습니다. s1.major와 s2.major를 출력해 보겠습니다.
Language
Language
동일한 결과가 출력되었습니다. s2의 major는 s1의 major와 같은 주소값이기 때문에 s2의 major를 바꾸면서 s1의 major값도 "Language"로 변경되었습니다. 이런 얕은 복사를 하지 않고 깊은 복사를 하기 위해서 복사 생성자를 정의합니다.
복사 생성자
복사 생성자의 형태를 먼저 보겠습니다.
student(const student& s)
{
num = s.num;
major = new char[strlen(s.major)+1];
strcpy(major, s.major);
}
num과 major는 기존 student 클래스에 있던 멤버 변수입니다. 그리고 s.num과 s.major는 복사할 객체 s의 멤버 변수를 의미합니다. num에 s.num을 저장하는 것은 일반 변수이기 때문에 간단한 과정입니다. major에 s.major를 저장하는 과정은 '주소값'만 공유하지 않고 새로운 메모리를 할당하여 데이터를 복사하여 붙여넣는 것입니다. 그러므로 기존 객체의 멤버 변수 값을 복사하여 받는 새로운 객체도 다른 메모리를 할당받아 기존 객체의 멤버 변수와 별도의 메모리를 갖게 됩니다. 이러한 복사를 바로 '깊은 복사'라고 합니다.
깊은 복사
깊은 복사를 수행하는 코드를 전체적으로 보겠습니다.
class student{
public:
int num;
char *major;
void setMajor(const char *majors)
{
strcpy(major, majors);
}
student(int nums, const char *majors)
{
num = nums;
major = new char[strlen(majors)+1];
strcpy(major, majors);
}
student(const student& s)
{
num = s.num;
major = new char[strlen(s.major)+1];
strcpy(major, s.major);
}
};
int main()
{
student s1 = s1(1, "Education");
student s2(s1);
cout << s1.num << endl;
cout << s1.major << endl;
cout << s2.num << endl;
cout << s2.major << endl;
return 0;
}
student 클래스의 멤버 변수 int num과 char *major를 생성했습니다. 동적 할당한 major의 값을 변경하기 위해서 setMajor 함수를 생성해서 strcpy로 값을 변경하도록 했습니다.
student s1 = s1(1, "Education");
student s2(s1);
s1 객체를 생성할 때 인자로 1과 "Education"을 주어서 num은 1, major는 "Education"으로 저장되었습니다. 그리고 s2에 s1을 인자로 줘서 객체를 생성하면서 '복사 생성자'가 호출됩니다. 인자로 받은 s1의 num값인 1이 s2의 num에 저장되고, s1의 major 값인 "Education"이 s2의 major값에 저장됩니다. 이 때 복사 생성자에 정의한 동적 할당이 실행됩니다.
student(const student& s)
{
num = s.num;
major = new char[strlen(s.major)+1];
strcpy(major, s.major);
}
복사 생성자의 인자로 복사할 객체 s를 받습니다. num에 객체 s의 num을 저장하고, major에 s의 major의 길이+1 길이로 동작 할당을 하여 메모리를 할당합니다. 그리고 major에 s.major 값을 복사합니다. 그러면 깊은 복사가 실행되어 s2의 major에 새로운 메모리가 할당되어 s1과 다른 메모리를 갖게 됩니다.
s2.setMajor("Language");
cout << s1.major << endl;
cout << s2.major << endl;
s2의 major 값을 "Language"로 변경하고, s1과 s2의 major 값을 각각 출력해 보았습니다.
Education
Language
s1의 major값은 Education이고, s2의 major값은 Language로 출력되었습니다. s2 객체 생성시 major의 메모리를 새롭게 할당했기 때문에 s1의 값과 s2의 값은 서로 다른 값을 유지하게 됩니다.
그럼 오늘은 얕은 복사와 깊은 복사의 차이를 알아보고, 복사 생성자를 정의하는 방법을 알아보았습니다.
감사합니다!
'C++' 카테고리의 다른 글
C++ 복사 생성자와 디폴트 복사 생성자 (1) | 2023.03.02 |
---|---|
C++ 복사 생성자 호출하기 (0) | 2023.02.27 |
C++ 얕은 복사와 복사 생성자 (0) | 2023.02.25 |
C++ 객체 배열 공부하기 / 여러 개의 객체를 한 번에 생성하기 (0) | 2023.02.22 |
C++ 큐(queue) 공부하기 / First In First Out (0) | 2023.02.21 |