✔️ 메모리 값을 그대로 복사하는 방식
✔️ 객체 내부의 포인터가 같은 메모리 주소를 가리키게 됨
✔️ 동일한 객체를 가리키므로 메모리 관리 문제 발생 가능
Knight knight1;
Knight knight2 = knight1; // 복사 생성자 (기본적으로 얕은 복사)
이렇게 하면 knight1과 knight2가 동일한 메모리를 공유하는 상태가 됩니다.
✔️ 새로운 메모리를 할당하여 복사하는 방식
✔️ 객체 내부에 포인터가 있다면, 새로운 객체를 생성하고 복사
✔️ 각 객체가 독립적인 메모리를 가짐 → 안전한 메모리 관리
Knight::Knight(const Knight& knight) {
_hp = knight._hp;
_pet = new Pet(*(knight._pet)); // 깊은 복사: 새로운 메모리 할당
}
이렇게 하면 knight1과 knight2는 서로 다른 _pet을 가지게 되어 독립적으로 동작합니다.
#include <iostream>
using namespace std;
// [Pet 클래스] : Knight가 소유하는 반려동물 클래스
class Pet {
public:
Pet() { cout << "Pet()" << endl; }
~Pet() { cout << "~Pet()" << endl; }
// 복사 생성자
Pet(const Pet& pet) { cout << "Pet(const Pet& pet)" << endl; }
// 복사 대입 연산자
Pet& operator=(const Pet& pet) {
cout << "operator=(const Pet& pet)" << endl;
return *this;
}
};
// [Player 클래스] : Knight의 부모 클래스
class Player {
public:
Player() { cout << "Player()" << endl; }
// 복사 생성자
Player(const Player& player) {
cout << "Player(const Player& player)" << endl;
_level = player._level;
}
// 복사 대입 연산자
Player& operator=(const Player& player) {
cout << "operator=(const Player& player)" << endl;
_level = player._level;
return *this;
}
public:
int _level = 0; // 플레이어 레벨
};
// [Knight 클래스] : Player를 상속받음
class Knight : public Player {
public:
Knight() {
_pet = new Pet(); // Pet 객체를 동적 할당
}
// [복사 생성자] - 깊은 복사 구현
Knight(const Knight& knight) : Player(knight) {
cout << "Knight(const Knight& knight)" << endl;
_hp = knight._hp;
_pet = new Pet(*(knight._pet)); // 깊은 복사: 새로운 메모리 할당
}
// [복사 대입 연산자] - 깊은 복사 구현
Knight& operator=(const Knight& knight) {
cout << "operator=(const Knight& knight)" << endl;
// 부모 클래스 복사 대입 연산자 호출
Player::operator=(knight);
// 기존 메모리 삭제 후 새로운 메모리 할당
if (_pet != nullptr) {
delete _pet;
}
_pet = new Pet(*(knight._pet));
_hp = knight._hp;
return *this;
}
~Knight() {
delete _pet; // 동적 할당된 메모리 해제
}
public:
int _hp = 100; // 기사 체력
Pet* _pet; // 반려동물 (포인터)
};
int main() {
// [1] Knight 객체 생성
Knight knight;
knight._hp = 200;
knight._level = 99;
cout << "---- 복사 생성자 호출 ----" << endl;
Knight knight2 = knight; // 복사 생성자 호출
cout << "---- 복사 대입 연산자 호출 ----" << endl;
Knight knight3; // 기본 생성자
knight3 = knight; // 복사 대입 연산자 호출
return 0;
}
실행 결과:
Pet()
Player()
---- 복사 생성자 호출 ----
Player(const Player&)
Pet(const Pet& pet)
Knight(const Knight&)
---- 복사 대입 연산자 호출 ----
operator=(const Player&)
operator=(const Pet&)
operator=(const Knight&)
Player(const Player&)가 실행됨 (부모 클래스의 복사 생성자)Pet(const Pet&) 실행됨 (깊은 복사 수행)Knight(const Knight&) 실행됨_hp 복사operator=(const Player&) 실행됨_pet을 삭제하고 새로운 _pet을 생성 (깊은 복사 수행)operator=(const Knight&) 실행됨_hp 복사Knight knight1;
Knight knight2 = knight1; // 얕은 복사
✔️ knight1과 knight2가 같은 _pet을 공유하게 됨
✔️ knight1이 소멸될 때 _pet이 삭제되면, knight2의 _pet도 손실됨 (메모리 접근 오류 발생)
delete _pet;이 두 번 호출됨)// 깊은 복사: 새로운 객체를 생성하여 복사
_pet = new Pet(*(knight._pet));
✔️ 각 객체가 독립적인 메모리를 가짐
✔️ 객체 하나가 삭제되더라도 다른 객체에 영향 없음
✔️ 메모리 누수 및 접근 오류 방지