*얕은 복사 ( 컴파일러가 재정의되지 않아도 자동으로 생성 )
=> 데이터를 단지 복사.
=> 멤버 데이터를 비트열 단위로 '똑같이' 복사 ( 메모리 영역 값을 그대로 복사 )
*깊은 복사 ( 객체 내 멤버변수로 포인터 & 참조 가 존재할 때 )
복사생성자 / 복사 대입연산자 재정의
=> 클래스 내의 포인터가 존재하고 그 포인터에 생성하는 클래스마다 각기 다른 주소를 담아야한다면
명시적 복사 생성자 / 복사 대입연산자를 정의해야 한다.
*해당 클래스에 다른 클래스를 단지 선언만 해준다면 어떤 문제가 있나 ??
1. 선언된 클래스가 크기가 크다면 해당 클래스도 비대해짐.
2. 해당 클래스가 소멸될 때 선언된 클래스도 같이 사라짐 => 생성/삭제 시점을 관리할 수 없음.
=> 생명주기 관리가 어려워짐.
3. 선언된 클래스에 상속된 자식 클래스를 가지고 있을 수 없다.
포인터* 로는 업캐스팅이 가능하기 때문에 자식클래스로도 접근 가능.
Knight(const Knight& other)
{
_pet = new Pet(*(other._pet));
_hp = other._hp;
}
Knight& operator=(const Knight& other)
{
if (this == &other) return;
delete _pet;
_pet = new Pet(*(other._pet));
_hp = other._hp;
return *this;
}
* & 로는 변수로 못 들고 있나 ??
=> 가능.
하지만, 참조형변수는 즉시 초기화 시켜주어야 하기 때문에 생성자 초기화 목록을 이용해야함.
'선처리 영역' 을 이용해 생성자에 들어가기 전 선언해주어야함.
또한,
한 번 설정하면 바꿀 수 없기 때문에, 복사, 대입 연산을 잘 수행할 수 없다.
결국, 포인터* 를 이용하자.
@컴파일러가 암시적 / 명시적 복사 생성자, 대입 연산자 실행 시, 차이점
*복사 생성자 실행 시,
*암시적 복사 생성자
1. 부모 클래스의 복사 생성자 호출
2. 멤버 클래스의 복사 생성자 호출
3. 멤버가 기본 타입일 경우 메모리 복사 ( 얕은 복사 )
*'명시적' 복사 생성자
1. 부모 클래스의 기본 생성자 호출
2. 멤버 클래스의 기본 생성자 호출
=> 해당 클래스의 복사생성자를 명시적 으로 정의하면,
부모 / 멤버 에 대한 복사생성자는 호출하지 않음 !!
=> 자식 클래스의 명시적으로 선언된 복사 생성자가 호출될 때,
'선처리 영역' 에서 부모, 멤버 클래스의 기본 생성자를 호출하고 있다.
따라서,
명시적으로 복사 생성자를 구현할 때,
해당 클래스 내의 객체와 상속관계에서 부모의 복사 생성자를 각각 호출해주어야 함.
Knight(const Knight& other)
: Player(other), _pet(other._pet)
{
cout << "Knight 복사 생성자" << endl;
}
*대입 복사 연산자 실행 시,
*암시적 복사 대입 연산자
1. 부모 클래스의 복사 대입 연산자 호출
2. 멤버 클래스의 복사 대입 연산자 호출
3. 멤버가 기본 타입일 경우 메모리 복사 ( 얕은 복사 )
*'명시적' 복사 대입 연산자
1. 부모 클래스의 기본 생성자 호출
2. 멤버 클래스의 기본 생성자 호출
Knight& operator=(const Knight& other)
{
Player::operator=(other);
_pet = other._pet;
cout << "Knight 복사 대입 연산자" << endl;
return *this;
}
=> 부모의 대입 복사 연산자를 호출해야함.
Player::operator=(other);
'프로그래밍 > C++' 카테고리의 다른 글
#49. 전방 선언 (0) | 2022.07.20 |
---|---|
#48. C++ 캐스팅 (0) | 2022.07.20 |
#46. 타입변환 (0) | 2022.07.20 |
#45. 동적할당 (0) | 2022.07.20 |
#44. 연산자 오버로딩 (0) | 2022.07.20 |