디폴트 복사 생성자와 디폴트 대입 연산자를 사용하면(컴파일러가 만듬)
상속 받았다면 부모 클래스의 복사 생성자 혹은 대입 연산자
멤버 중 객체가 있다면 멤버의 복사 생성자 혹은 대입 연산자를 호출한다.
멤버의 값을 그대로 복사하기 때문에 (포인터 같은) 주소 값을 가진 멤버가 있으면 문제가 생길 수 있다.
위를 얕은 복사라 하고
주소 값을 복사하는 것이 아닌 주소가 가르키는 값을 복사해 오는 것이 깊은 복사이다.
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;
}
결과