얕은 복사, 깊은 복사

Oak_Cassia·2021년 11월 23일
0

디폴트 복사 생성자와 디폴트 대입 연산자를 사용하면(컴파일러가 만듬)
상속 받았다면 부모 클래스의 복사 생성자 혹은 대입 연산자
멤버 중 객체가 있다면 멤버의 복사 생성자 혹은 대입 연산자를 호출한다.

멤버의 값을 그대로 복사하기 때문에 (포인터 같은) 주소 값을 가진 멤버가 있으면 문제가 생길 수 있다.
위를 얕은 복사라 하고

주소 값을 복사하는 것이 아닌 주소가 가르키는 값을 복사해 오는 것이 깊은 복사이다.

int main()
{
	AppleTree a1;  //Tree를 부모로 Fruite를 멤버로 가진 객체
	a1.height = 10;
	AppleTree a2;
	a2 = a1;
	
}

실행결과

기본 대입 연산자 호출 결과
1. 부모 클래스의 대입 연산자 호출
2. 멤버로 가진 객체의 대입 연산자 호출

int main()
{
	AppleTree a1;
	a1.height = 10;
	AppleTree a2 = a1;
	
}


기본 복사 생성자 호출 결과
1. 부모 클래스의 복사 생성자 호출
2. 멤버로 가진 객체의 복사 생성자 호출


위 두 경우

class AppleTree : public Tree
{
public:
	int number;
	Fruit* apple;
    
    
    ...
    
    
};

a1과 a2의 각각의 멤버 객체 포인터 Fruit* apple에 같은 값이 저장된다.


AppleTree()
	{
		cout << "AppleTree()" << endl;
		number = 0;
		apple = new Fruit();
	}
    
~AppleTree()
	{
		delete apple;
	}

생성자와 소멸자로 동적할당을 관리하는 경우 double free가 발생!!!

이러한 경우 대입 연산자와 복사 생성자를 오버라이딩해야 한다.

오버로딩한 복사 생성자

int main()
{
	AppleTree a1;
	a1.height = 10;
	AppleTree a2=a1;	
}

멤버 Fruit*를 Fruit로 고치고 동적할당 부분을 주석처리 했다.
1. 부모 클래스의 기본 생성자
2. 멤버로 가진 객체의 기본 생성자 호출

복사 생성자를 호출하려면

AppleTree(const AppleTree& ap1) : Tree(ap1), apple(ap1.apple)
	{
		number = ap1.number;
		//apple = new Fruit(*ap1.apple);
		cout << "AppleTree(const AppleTree& ap1)" << endl;
		
		
	}

오버로딩한 대입 연산자

int main()
{
	AppleTree a1;
	a1.height = 10;
	AppleTree a2;
	a2 = a1;
	
}


아무것도 호출되지 않았다.

대입 연산자를 호출하려면

AppleTree& operator=(const AppleTree& ap1)
	{
		cout << "AppleTree& operator=(const AppleTree& ap1)" << endl;
		number = ap1.number;
		apple = ap1.apple;
		Tree::operator=(ap1);
		
		return *this;
	}

전체코드

#include <iostream>
using namespace std;

class Tree
{
public:
	int height;

	Tree& operator=(const Tree& t)
	{
		height = t.height;
		cout << "Tree& =" << endl;
		return *this;
	}
	Tree(const Tree& t)
	{
		height = t.height;
		cout << "Tree(const Tree&)" << endl;
	}
	Tree()
	{
		cout << "Tree()" << endl;
		height = 0;
	}
};

class Fruit
{
public:
	int weight;
	Fruit& operator=(const Fruit& t)
	{
		weight = t.weight;
		cout << "Fruit& =" << endl;
		return *this;
	}
	Fruit(const Fruit& t)
	{
		weight = t.weight;
		cout << "Fruit(const Tree&)" << endl;
	}

	Fruit()
	{
		cout << "Fruit()" << endl;
		weight = 0;
	}
};

class AppleTree : public Tree
{
public:
	int number;
	Fruit apple;
	AppleTree()
	{
		cout << "AppleTree()" << endl;
		number = 0;
		//apple = new Fruit();
	}
	~AppleTree()
	{
		//delete apple;
	}

	AppleTree(const AppleTree& ap1) : Tree(ap1), apple(ap1.apple)
	{
		number = ap1.number;
		//apple = new Fruit(*ap1.apple);
		cout << "AppleTree(const AppleTree& ap1)" << endl;
		
		
	}
	AppleTree& operator=(const AppleTree& ap1)
	{
		cout << "AppleTree& operator=(const AppleTree& ap1)" << endl;
		number = ap1.number;
		apple = ap1.apple;
		Tree::operator=(ap1);
		
		return *this;
	}
	
};


int main()
{
	AppleTree a1;
	a1.height = 10;
	AppleTree a2;
	a2 = a1;

	cout << "----------위는 대입 연산자 밑은 복사 생성자------------" << endl;

	AppleTree a3 = a1;
	
}

결과

profile
rust로 뭐할까

0개의 댓글