복사 생성자를 정의해야 하는 이유

2019. 8. 21. 01:50PL/C++

"멤버 대 멤버의 복사가 진행되는 디폴트 복사 생성자가 자동으로 삽입되니까, 굳이 복사 생성자를 직접 정의할 필요는 없지 않을까"라고 생각할 수 있다.

 

#include <cstring>
#include <iostream>

using namespace std;

class Person 
{
private:
	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 << '\n';
		cout << "나이: " << age << '\n';
	}
	~Person() {
		delete []name;
		cout << "called destructor!\n";
	}
};

int main(void) {
	Person man1("Lee dong woo", 29);
	Person man2 = man1;
	man1.ShowPersonInfo();
	man2.ShowPersonInfo();
	return 0;
}

 

위 코드를 작성해서, 실행시키면 빌드 오류가 생길 것이다. 기본적으로 디폴트 복사 생성자는 '얕은 복사' 기반으로 이뤄진다. '얕은 복사'는 힙 영역에서 문제가 생긴다. 힙 영역으로 선언된 변수에 대해서 새로 생성된 객체와 기존 객체를 같이 참조하는 형태로 이뤄진다. 따라서 소멸할 때, 먼저 소멸하는 객체는 문제없이 delete를 한다. 하지만 뒤이어 소멸되는 객체는 참조하던 힙 영역의 변수가 사라졌기 때문에 빌드 오류를 발생시킨다

 

따라서 힙 영역의 변수를 다룰 땐 반드시 복사 생성자를 재정의해야 한다. 어려운 코드를 넣는 것이 아니라 말 그대로 변수를 하나하나 복사하는 코드를 작성하면 된다

 

 

#include <cstring>
#include <iostream>

using namespace std;

class Person 
{
private:
	char *name;
	int age;

public:
	Person(char *myname, int myage) {
		int len = strlen(myname) + 1;
		name = new char[len];
		strcpy(name, myname);
		age = myage;
	}
	Person(const Person &rhs) {
		name = new char[strlen(rhs.name) + 1];
		strcpy(name, rhs.name);
		age = rhs.age;
	}
	void ShowPersonInfo() const {
		cout << "이름: " << name << '\n';
		cout << "나이: " << age << '\n';
	}
	~Person() {
		delete []name;
		cout << "called destructor!\n";
	}
};

int main(void) {
	Person man1("Lee dong woo", 29);
	Person man2 = man1;
	man1.ShowPersonInfo();
	man2.ShowPersonInfo();
	return 0;
}

 

[출처] 윤성우, 열혈강의 C++ 프로그래밍

'PL > C++' 카테고리의 다른 글

const 키워드  (0) 2019.08.21
임시객체  (0) 2019.08.21
디폴트 생성자가 사라지는 경우  (0) 2019.08.21
복사생성자와 치환연산자를 정의할 때 주의할 점  (0) 2019.08.20
네임스페이스  (0) 2019.08.20