전체 코드

1. 얕은 복사 vs 깊은 복사 개념

(1) 얕은 복사 (Shallow Copy)

✔️ 메모리 값을 그대로 복사하는 방식
✔️ 객체 내부의 포인터가 같은 메모리 주소를 가리키게 됨
✔️ 동일한 객체를 가리키므로 메모리 관리 문제 발생 가능

Knight knight1;
Knight knight2 = knight1; // 복사 생성자 (기본적으로 얕은 복사)

이렇게 하면 knight1과 knight2가 동일한 메모리를 공유하는 상태가 됩니다.


(2) 깊은 복사 (Deep Copy)

✔️ 새로운 메모리를 할당하여 복사하는 방식
✔️ 객체 내부에 포인터가 있다면, 새로운 객체를 생성하고 복사
✔️ 각 객체가 독립적인 메모리를 가짐 → 안전한 메모리 관리

Knight::Knight(const Knight& knight) {
    _hp = knight._hp;
    _pet = new Pet(*(knight._pet)); // 깊은 복사: 새로운 메모리 할당
}

이렇게 하면 knight1knight2는 서로 다른 _pet을 가지게 되어 독립적으로 동작합니다.


2. 코드 분석 및 주석 설명

#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;
}

3. 실행 결과 분석

실행 결과:

Pet()
Player()
---- 복사 생성자 호출 ----
Player(const Player&)
Pet(const Pet& pet)
Knight(const Knight&)
---- 복사 대입 연산자 호출 ----
operator=(const Player&)
operator=(const Pet&)
operator=(const Knight&)

(1) 복사 생성자 호출 과정

  1. 부모 클래스 복사 생성자 호출
    • Player(const Player&)가 실행됨 (부모 클래스의 복사 생성자)
  2. 멤버 클래스 복사 생성자 호출
    • Pet(const Pet&) 실행됨 (깊은 복사 수행)
  3. Knight 복사 생성자 실행
    • Knight(const Knight&) 실행됨
    • _hp 복사

(2) 복사 대입 연산자 호출 과정

  1. 부모 클래스의 복사 대입 연산자 호출
    • operator=(const Player&) 실행됨
  2. 멤버 클래스의 복사 대입 연산자 호출
    • 기존의 _pet을 삭제하고 새로운 _pet을 생성 (깊은 복사 수행)
  3. Knight 복사 대입 연산자 실행
    • operator=(const Knight&) 실행됨
    • _hp 복사

4. 얕은 복사(Shallow Copy) 문제점

Knight knight1;
Knight knight2 = knight1;  // 얕은 복사

✔️ knight1과 knight2가 같은 _pet을 공유하게 됨
✔️ knight1이 소멸될 때 _pet이 삭제되면, knight2의 _pet도 손실됨 (메모리 접근 오류 발생)

🚨 얕은 복사의 문제

  • 동일한 객체를 공유하므로 메모리 관리 문제 발생
  • 객체 하나가 삭제되면 다른 객체의 멤버도 영향을 받음
  • double delete(이중 삭제) 오류 발생 가능 (delete _pet;이 두 번 호출됨)

5. 깊은 복사(Deep Copy)의 해결책

// 깊은 복사: 새로운 객체를 생성하여 복사
_pet = new Pet(*(knight._pet));

✔️ 각 객체가 독립적인 메모리를 가짐
✔️ 객체 하나가 삭제되더라도 다른 객체에 영향 없음
✔️ 메모리 누수 및 접근 오류 방지


profile
李家네_공부방

0개의 댓글