얕은 복사 vs 깊은 복사 2

 

인프런 Rookiss님의 'Part1: C++ 프로그래밍 입문' 강의를 기반으로 정리한 필기입니다. 
😎[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 강의 들으러 가기!

 

 


 

1. 암시적 복사 생성자 Steps

 

1) 부모 클래스의 복사 생성자 호출

2) 멤버 클래스의 복사 생성자 호출

3) 멤버가 기본 타입일 경우 메모리 복사 (얕은 복사 Shallow Copy)

더보기
#include <iostream>
using namespace std;

// 얕은 복사 vs 깊은 복사 2

class Pet{
public:
    Pet() { cout << "Pet()" << endl;  }
    ~Pet()  { cout << "~Pet()" << endl;  }
    Pet(const Pet& pet)  { cout << "Pet(const Pet& pet)" << endl; }
};

class Player{
public:
    Player()  { cout << "Player()" << endl;  }

    // 복사 생성자
    Player(const Player& player)  {
        cout << "Player(const Player&)" << endl;
        _level = player._level;
    }

    // 복사 대입 연산자
    Player& operator=(const Player& player)  {
        cout << "operator=(const Player&)" << endl;
        _level = player._level;
        return *this;
    }

public:
    int _level = 0;
};

class Knight : public Player{
public:
    Knight() { }
    ~Knight() { }

public:
    int _hp = 100;
    Pet _pet;
};

int main()
{
    Knight knight;  // 기본 생성자
    knight._hp = 200;
    knight._level = 99;
    
    cout << "-------- 복사 생성자 ----------" << endl;
    Knight knight2 = knight;  // 복사 생성자
    //Knight knight3(knight);

    cout << "-------- 복사 대입 연산자 ----------" << endl;
    Knight knight3;   // 기본 생성자
    knight3 = knight; // 복사 대입 연산자
}

 

1) 부모 클래스의 복사 생성자 호출

2) 멤버 클래스의 복사 생성자 호출

3) 멤버가 기본 타입일 경우 메모리 복사 (얕은 복사 Shallow Copy)

 

 

 

 


 

 

2. 명시적 복사 생성자 Steps


1) 부모 클래스의 기본 생성자 호출

2) 멤버 클래스의 기본 생성자 호출

더보기
#inlclude <iostream>
using namespace std;

class Pet{
public:
    Pet() { cout << "Pet()" << endl;  }
    ~Pet()  { cout << "~Pet()" << endl;  }
    Pet(const Pet& pet)  { cout << "Pet(const Pet& pet)" << endl; }
};

class Player{
public:
    Player()  { cout << "Player()" << endl;  }

    // 복사 생성자
    Player(const Player& player)  {
        cout << "Player(const Player&)" << endl;
        _level = player._level;
    }

    // 복사 대입 연산자
    Player& operator=(const Player& player)  {
        cout << "operator=(const Player&)" << endl;
        _level = player._level;
        return *this;
    }

public:
    int _level = 0;
};

class Knight : public Player{
public:
    Knight() { }
    Knight(const Knight& knight){ // 복사 생성자
        cout << "Knight(const Knight&)" << endl;

        _hp = knight._hp;
    }
    ~Knight() { }

public:
    int _hp = 100;
    Pet _pet;
};

int main()
{
    Knight knight;  // 기본 생성자
    knight._hp = 200;
    knight._level = 99;
    
    cout << "-------- 복사 생성자 ----------" << endl;
    Knight knight2 = knight;  // 복사 생성자
    //Knight knight3(knight);

    cout << "-------- 복사 대입 연산자 ----------" << endl;
    Knight knight3;   // 기본 생성자
    knight3 = knight; // 복사 대입 연산자
}

 

1) 부모 클래스의 기본 생성자 호출

2) 멤버 클래스의 기본 생성자 호출

 

 

 

 

 

 

 

 

 

더보기
#inlclude <iostream>
using namespace std;

class Pet{
public:
    Pet() { cout << "Pet()" << endl;  }
    ~Pet()  { cout << "~Pet()" << endl;  }
    Pet(const Pet& pet)  { cout << "Pet(const Pet& pet)" << endl; }
};

class Player{
public:
    Player()  { cout << "Player()" << endl;  }

    // 복사 생성자
    Player(const Player& player)  {
        cout << "Player(const Player&)" << endl;
        _level = player._level;
    }

    // 복사 대입 연산자
    Player& operator=(const Player& player)  {
        cout << "operator=(const Player&)" << endl;
        _level = player._level;
        return *this;
    }

public:
    int _level = 0;
};

class Knight : public Player{
public:
    Knight() { }
    
   	Knight(const Knight& knight) : Player(knight), _pet(knight._pet)   // Player(knight) 부모 클래스의 복사생성자 방식의 호출
    {
        cout << "Knight(const Knight&)" << endl;

        _hp = knight._hp;        
        _stamina = knight._stamina;
    }
    
    ~Knight() { }

public:
    int _hp = 100;
    Pet _pet;
};

int main()
{
    Knight knight;  // 기본 생성자
    knight._hp = 200;
    knight._level = 99;
    
    cout << "-------- 복사 생성자 ----------" << endl;
    Knight knight2 = knight;  // 복사 생성자
    //Knight knight3(knight);

    cout << "-------- 복사 대입 연산자 ----------" << endl;
    Knight knight3;   // 기본 생성자
    knight3 = knight; // 복사 대입 연산자
}

Player(knight) 부모 클래스의 복사생성자 방식의 호출


 

 

 

3. 암시적 복사 대입 연산자 Steps

 

1) 부모 클래스의 복사 생성자 호출

2) 멤버 클래스의 복사 생성자 호출

3) 멤버가 기본 타입일 경우 메모리 복사 (얕은 복사 Shallow Copy)

 

더보기
#inlclude <iostream>
using namespace std;

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&" << endl;
        return *this;
    }
};

class Player{
public:
    Player()  { cout << "Player()" << endl;  }

    // 복사 생성자
    Player(const Player& player)  {
        cout << "Player(const Player&)" << endl;
        _level = player._level;
    }

    // 복사 대입 연산자
    Player& operator=(const Player& player)  {
        cout << "operator=(const Player&)" << endl;
        _level = player._level;
        return *this;
    }

public:
    int _level = 0;
};

class Knight : public Player{
public:
    Knight() { }
    
   	Knight(const Knight& knight) : Player(knight), _pet(knight._pet)   // Player(knight) 부모 클래스의 복사생성자 방식의 호출
    {
        cout << "Knight(const Knight&)" << endl;

        _hp = knight._hp;        
        _stamina = knight._stamina;
    }
    
    ~Knight() { }

public:
    int _hp = 100;
    Pet _pet;
};

int main()
{
    Knight knight;  // 기본 생성자
    knight._hp = 200;
    knight._level = 99;
    
    Knight knight3;   // 기본 생성자
    
    cout << "-------- 복사 대입 연산자 ----------" << endl;
    knight3 = knight; // 복사 대입 연산자
}

 

1) 부모 클래스의 복사 생성자 호출

     operator=(const Player&)

2) 멤버 클래스의 복사 생성자 호출

    operator=(const Pet&)

3) 멤버가 기본 타입일 경우 메모리 복사 (얕은 복사 Shallow Copy)

 

 


 

 

4. 명시적 복사 대입 연산자 Steps

 

1) 알아서 해주는거 없음

 

전문가를 위한 C++ 책 p486~488 참조

 

더보기
#inlclude <iostream>
using namespace std;

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&" << endl;
        return *this;
    }
};

class Player{
public:
    Player()  { cout << "Player()" << endl;  }

    // 복사 생성자
    Player(const Player& player)  {
        cout << "Player(const Player&)" << endl;
        _level = player._level;
    }

    // 복사 대입 연산자
    Player& operator=(const Player& player)  {
        cout << "operator=(const Player&)" << endl;
        _level = player._level;
        return *this;
    }

public:
    int _level = 0;
};

class Knight : public Player{
public:
    Knight() { }
    
   	Knight(const Knight& knight) : Player(knight), _pet(knight._pet)   // Player(knight) 부모 클래스의 복사생성자 방식의 호출
    {
        cout << "Knight(const Knight&)" << endl;

        _hp = knight._hp;        
        _stamina = knight._stamina;
    }

    Knight& operator=(const Knight& knight)
    {
        cout << "operator=(const Knight&)" << endl;

        Player::operator=(knight); // 명시적 요청
        _pet = knight._pet; // 대입 연산자 명시적 요청

        _hp = knight._hp;
        return *this;
    }

    ~Knight() { }

public:
    int _hp = 100;
    Pet _pet;
};

int main()
{
    Knight knight;  // 기본 생성자
    knight._hp = 200;
    knight._level = 99;
    
    Knight knight3;   // 기본 생성자
    
    cout << "-------- 복사 대입 연산자 ----------" << endl;
    knight3 = knight; // 복사 대입 연산자
}

 

 

 

 


 

 

 

정리

 

왜 이렇게 혼란스러울까?

 

객체를 '복사' 한다는 것은 두 객체의 값들을 일치시키려는 것

따라서 기본적으로 얕은 복사 (Shallow Copy) 방식으로 동작

 

깊은 복사를 사용하려면

-  명시적 복사를 사용한다.

-  명시적 복사 -> [모든 책임]을 프로그래머한테 위임하겠다는 의미

 

 

더보기
#include <iostream>
using namespace std;

// 얕은 복사 vs 깊은 복사 2

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&" << endl;
        return *this;
    }

};

class Player
{
public:
    Player()
    {
        cout << "Player()" << endl;
    }

    // 복사 생성자
    Player(const Player& player)
    {
        cout << "Player(const Player&)" << endl;
        _level = player._level;
    }

    // 복사 대입 연산자
    Player& operator=(const Player& player)
    {
        cout << "operator=(const Player&)" << endl;
        _level = player._level;
        return *this;
    }

public:
    int _level = 0;
};


class Knight : public Player
{
public:
    Knight()
    {
        
    }

    Knight(const Knight& knight) : Player(knight), _pet(knight._pet)   // Player(knight) 부모 클래스의 복사생성자 방식의 호출
    {
        cout << "Knight(const Knight&)" << endl;

        _hp = knight._hp;        
        _stamina = knight._stamina;
    }

    Knight& operator=(const Knight& knight)
    {
        cout << "operator=(const Knight&)" << endl;

        Player::operator=(knight);
        _pet = knight._pet;

        _hp = knight._hp;
        return *this;
    }

    ~Knight()
    {
        
    }

public:
    int _hp = 100;
    int _stamina = 100;

    Pet _pet;
};


int main()
{
    Knight knight;  // 기본 생성자
    knight._hp = 200;
    knight._level = 99;
    
    //cout << "-------- 복사 생성자 ----------" << endl;
    //Knight knight2 = knight;  // 복사 생성자
    //Knight knight3(knight);

    
    Knight knight3;   // 기본 생성자

    cout << "-------- 복사 대입 연산자 ----------" << endl;
    knight3 = knight; // 복사 대입 연산자

    // 실험)
    // - 암시적 복사 생성자 Steps
    // 1) 부모 클래스의 복사 생성자 호출
    // 2) 멤버 클래스의 복사 생성자 호출
    // 3) 멤버가 기본 타입일 경우 메모리 복사 (얕은 복사 Shallow Copy)
     
    // - 명시적 복사 생성자 Steps
    // 1) 부모 클래스의 기본 생성자 호출
    // 2) 멤버 클래스의 기본 생성자 호출


    // - 암시적 복사 대입 연산자 Steps
    // 1) 부모 클래스의 복사 생성자 호출
    // 2) 멤버 클래스의 복사 생성자 호출
    // 3) 멤버가 기본 타입일 경우 메모리 복사 (얕은 복사 Shallow Copy)
    
    // - 명시적 복사 대입 연산자 Steps
    // 1) 알아서 해주는거 없음

    // 왜 이렇게 혼란스러울까?
    // 객체를 '복사' 한다는 것은 두 객체의 값들을 일치시키려는 것
    // 따라서 기본적으로 얕은 복사 (Shallow Copy) 방식으로 동작

    // 명시적 복사 -> [모든 책임]을 프로그래머한테 위임하겠다는 의미


    return 0;
}