IT 프로그래밍/객체지향프로그래밍

객체의 복사와 삭제

기술1 2024. 6. 17. 10:00
반응형
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;


class MyRectangle {
public:
	MyPoint* lu;
	double width, height;
	MyRectangle() = default;
	MyRectangle(double x, double y, double w,double h):
		lu(new MyPoint(x, y)), width(w), height(h){}
};

MyRectangle r(1, 2, 3, 4);
MyRectangle q = r;
r.lu->x = 100;
cout << q.lu->x << endl;

c++에서 객체를 복사하면 멤버별 복사가 일어나빈다. 객체 r의 멤버 lu는 MyPoint객체의 주소이므로 그 주소가 객체 q의 멤버 lu로 복사고딥니다. 

 

그 결과 r의 lu의 좌표를 100으로 변경하면 q의 lu의 좌표도 100으로 변경됩니다. 

 

객체의 삭제 garbage

void somdFunc() {
	MyRectangle r(1, 2, 3, 4);
	MyRectangle* q = new MyRectangle(4, 5, 6, 7);

	//do something
	
	delete q;
}

여기서 함수가 종료되면 지역변수인 r이 삭제됩니다. 하지만 r.lu가 가리키던 MyPoint 객체는 삭제되지 않습니다. 또한 q가 가리키던 객체를 delete했지만, q->lu가 가리키던 MyPoint 객체는 삭제되지 않습니다. 

 

이 객체들의 주소는 아무도 가지고 있지 않으므로 사용이 불가능하며 이는 garbage가 됩니다. 

 

복사 생성자

어떤 객체를 동일한 타입의 이미 존재하는 다른 객체를 복사하여 생성하는 것을 복사 생성자라고 합니다. 

 

복사 생성을 담당하는 생성자를 복사생성자라고 합니다. 

MyPoint p(1, 2);
MyPoint q = p;
MyPoint r(p);
MyPoint s{ p };
MyPoint* t - new MyPoint(p);
MyPoint t;
t = p;

q = p 부터 new MyPoint(p) 모두 복사생성자가 실행됩니다. 

 

t = p 마지막 같은 경우는 객체가 생성되는 것이 아니므로 복사생성자가 실행되지 않습니다. 대신 치환(=) 연산자가 실행됩니다. 

 

어떤 경우 복사 생성자를 직접 만들어야 할까?

일반적으로 클래스가 포인터를 멤버로 갖지 않는다면 자동생성된 복사생성자로 충분하며 복사생성자를 따로 만들 필요는 없습니다. 포인터 멤버가 있을 경우 자동 생성된 복사생성자는 shallow copy를 하게 됩니다. 포인터 멤버가 가리키는 객체 자체는 복사되지 않고 포인터가 저장한 주소만 복사됩니다. 

 

따라서 원래의 객체와 복사된 객체가 동일한 객체를 가리키게 됩니다. Deep copy가 필요하다면 복사생성자를 직접 만들어야 합니다. 

class Foo {
public:
	Foo() = default;
	Foo(const Foo &f) { //copy constructor
	
	}
};

매개변수는 자신과 동일한 변수의 Foo 클래스의 f가 됩니다. 여기에는 두가지 포인트가 있는데 이때 이 매개변수는 참조여야 합니다. 복사생성자의 매개변수는 참조여야 하고 값에 의한 호출로는 안됩니다.

 

만약 foo 타입의 객체 f라면 값에 의한 호출이라는 것인데 생성을 할 때 주어지는 p라는 객체가 이 생성자에게 전달될 때 매개변수의 값이 복사되어서 된다면 복사를 해주는 애가 copy constructor인데 순환 오류가 생기는 것입니다. 

 

그래서 반드시 copy constructor의 매개변수는 반드시 참조여야 합니다. 두 번째로 기억해야 하는 포인트는 반드시 const로 선언을 해야 합니다. 매개변수로 주어지는 객체 자체가 const로 주어져야 하는 것은 아닙니다. 

 

 

class Foo {
public:
	int val;
	Foo() = default;
	Foo(int a) : val(a) {}
};

class Bar {
public:
	Foo* ptr = nullptr;
	vector<Foo*> vec;
	Bar() = default;
};

int main() {
	Bar a;
	a.ptr = new Foo(100);
	a.vec.push_back(new Foo(1));
	a.vec.push_back(new Foo(2));
	a.vec.push_back(new Foo(3));

	Bar b = a;
	a.ptr->val = 200;
	a.vec[0]->val = 300;

	cout << b.ptr->val << " " <<
		b.vec[0]->val << endl;

	return 0;
}

 

 

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

class Foo {
public:
	int val;
	Foo() = default;
	Foo(int a) : val(a) {}
};

class Bar {
public:
	Foo* ptr = nullptr;
	vector<Foo*> vec;
	Bar() = default;

	Bar(const Bar& t) {
		ptr = new Foo(*t.ptr);
		for (auto& pt : t.vec)
			vec.push_back(new Foo(*pt));
	}
};

int main() {
	Bar a;
	a.ptr = new Foo(100);
	a.vec.push_back(new Foo(1));
	a.vec.push_back(new Foo(2));
	a.vec.push_back(new Foo(3));

	Bar b = a;
	a.ptr->val = 200;
	a.vec[0]->val = 300;

	cout << b.ptr->val << " " <<
		b.vec[0]->val << endl;

	return 0;
}

Deep copy를 해준 것입니다. 

 

 

반응형

'IT 프로그래밍 > 객체지향프로그래밍' 카테고리의 다른 글

컴파일 과정  (0) 2024.06.18
복사생성자  (0) 2024.06.17
다항식 계산기 (code12. cpp 수정  (0) 2024.06.16
동적 생성 개체의 삭제와 쓰레기  (0) 2024.06.16
동적 객체 생성 new 연산자  (0) 2024.06.16