19. [C++] 깊은 복사와 얕은 복사
2024. 4. 15. 11:31ㆍ[C++]/C++ 언어 기초
디폴트 복사 생성자는 멤버 대 멤버의 복사를 진행한다.
이러한 방식의 복사를 '얕은 복사'라고 하는데, 이는 멤버변수가 힙의 메모리 공간을 참조하는 경우에 문제가 된다.
디폴트 복사 생성자의 문제점
ShallowCopyError.cpp
#include <iostream>
#include <cstring>
using namespace std;
class Person
{
char * name;
int age;
public:
Person(char * myname, int myage)
{
int len=strlen(myname)+1;
name=new char[len];
strcpy(name, myname);
age=myage;
}
void ShowPersonInfo() const
{
cout<<"이름: "<<name<<endl;
cout<<"나이: "<<age<<endl;
}
~Person()
{
delete []name;
cout<<"called destructor!"<<endl;
}
};
int main(void)
{
Person man1("Lee dong woo", 29);
Person man2=man1;
man1.ShowPersonInfo();
man2.ShowPersonInfo();
return 0;
}
이름: Lee dong woo
나이: 29
이름: Lee dong woo
나이: 29
called destructor!
- man1과 man2 두개의 객체를 생성했으므로, 소멸자도 두 번 호출되어야 하는데 실행 결과에서는 한번만 호출되어 있다.
- 이 문제는 얕은 복사에 있다.
Person man2 = man1;
- 이 문장에서 man2가 생성되면서 디폴트 복사 생성자가 호출된다.
- 하지만 디폴트 복사 생성자는 멤버 대 멤버를 단순히 복사만 하므로, 아래 그림의 구조를 띈다.
- 디폴트 복사 생성자는 멤버 대 멤버의 단순 복사를 진행하기 때문에 복사의 결과로 하나의 문자열을 두 개의 객체가 동시에 참조하는 꼴을 만든다.
- 이로 인해 객체의 소멸과정에서 문제가 발생한다.
delete []name;
- man2 객체가 먼저 소멸됐다고 가정했을 때, man1 객체의 멤버 name이 참조하는 문자열은 이미 소멸되어 delete 연산을 하는 데에 문제가 생긴다.
- 따라서 '깊은 복사'를 이용해 복사 생성자를 정의해야 한다.
‘깊은 복사’를 위한 복사 생성자의 정의
- '깊은 복사'는 멤버뿐만 아니라, 포인터로 참조하는 대상까지 깊게 복사한다.
- 얕은 복사와 달리 객체가 가진 모든 멤버를 복사하는 것을 말한다.
- 깊은 복사를 구성하는 복사 생성자이다.
- 멤버 변수 age의 멤버 대 멤버 복사가 이루어 진다.
- 메모리 공간 새로 동적할당 후, 문자열 복사, 그리고 할당된 메모리 주소 값을 멤버 name에 저장한다.
실습 문제
문제
문제04-3의 문제2를 통해서 NameCard클래스를 정의하였다. 그런데 이 클래스도 생성자 내에세 메모리 공간을 동적 할당하기 때문에 복사 생성자가 필요한 클래스이다. 이에 복사 생성자를 적절히 정의해보기 바라며, 복사 이후에 문제가 발생하지 않음을 다음 main함수를 통해서 확인 하길 바란다.
[main함수 예]
int main(void)
{
NameCard manClerk("Lee","ABEng","010-2342-1231",COMP_POS::CLERK);
NameCard copy1 = manClerk;
NameCard manSENIOR("Seo","APPLE","010-2342-1231",COMP_POS::SENIOR);
NameCard copy2 = manSENIOR;
copy1.ShowNameCardInfo();
copy2.ShowNameCardInfo();
return 0;
}
내 풀이
#include "stdafx.h"
#include <iostream>
#include <cstring>
using namespace System;
using namespace std;
namespace COMP_POS
{
enum{CLERK, SENIOR, ASSIST, MANAGER};
void ShowPositionInfo(int pos){
switch(pos){
case CLERK:
cout<<"사원"<<endl;
break;
case SENIOR:
cout<<"주임"<<endl;
break;
case ASSIST:
cout<<"대리"<<endl;
break;
case MANAGER:
cout<<"과장"<<endl;
break;
}
}
}
class NameCard{
private:
char * name;
char * company;
char * phone;
int position;
public:
NameCard(char * _name, char * _company, char * _phone, int pos):position(pos){
name = new char[strlen(_name)+1];
company = new char[strlen(_company)+1];
phone = new char[strlen(_phone)+1];
strcpy_s(name, strlen(_name) + 1, _name);
strcpy_s(company, strlen(_company) + 1, _company);
strcpy_s(phone, strlen(_phone) + 1, _phone);
}
NameCard(const NameCard& copy):position(copy.position){
name = new char[strlen(copy.name) + 1];
company = new char[strlen(copy.company)+1];
phone = new char[strlen(copy.phone)+1];
strcpy_s(name, strlen(copy.name) + 1, copy.name);
strcpy_s(company, strlen(copy.company) + 1, copy.company);
strcpy_s(phone, strlen(copy.phone) + 1, copy.phone);
}
void ShowNameCardInfo(){
cout<<"이름: "<<name<<endl;
cout<<"회사: "<<company<<endl;
cout<<"전화번호: "<<phone<<endl;
cout<<"직급: "; COMP_POS::ShowPositionInfo(position);
cout<<endl;
}
~NameCard(){
delete []name;
delete []company;
delete []phone;
}
};
int main (void)
{
NameCard manClerk("Lee", "ABCEng", "010-1111-2222", COMP_POS::CLERK);
NameCard copy1 = manClerk;
NameCard manSENIOR("Park", "OrangeEng", "010-3333-4444", COMP_POS::SENIOR);
NameCard copy2 = manSENIOR;
copy1.ShowNameCardInfo();
copy2.ShowNameCardInfo();
return 0;
}
반응형
'[C++] > C++ 언어 기초' 카테고리의 다른 글
은행계좌프로그램 실습 3단계 (0) | 2024.04.15 |
---|---|
20. [C++] 복사 생성자의 호출시점 (0) | 2024.04.15 |
18. [C++]복사 생성자(Copy Constructor) (0) | 2024.04.15 |
04-05 은행계좌문제 2단계 (0) | 2024.04.15 |
16. [C++] 생성자(Constructor)와 소멸자(Destructor) (0) | 2024.04.12 |