본문 바로가기
Language/C++

[c++][씹어먹는 c++] 4.3 스타만들기(복사 생성자, 소멸자)

by 담백로봇 2023. 10. 17.

https://modoocode.com/188

 

씹어먹는 C++ - <4 - 3. 스타크래프트를 만들자 ① (복사 생성자, 소멸자)>

모두의 코드 씹어먹는 C++ - <4 - 3. 스타크래프트를 만들자 ① (복사 생성자, 소멸자)> 작성일 : 2013-01-06 이 글은 96145 번 읽혔습니다. 이번 강좌에서는복사 생성자 (copy constructor) - 깊은 복사와 얕은

modoocode.com

주제:

  • 소멸자 (destructor)
  • 복사 생성자 (copy constructor) - 깊은 복사와 얕은 복사

체크포인트:

  • [특성] new 는 객체를 동적으로 생성하면서 동시에 생성자(constructor)를 자동 생성, 이후 delete 로 반드시 해제해야함.
    • int main() { Marine* marines[100];
    • marines[0] = new Marine(2, 3);
    • marines[1] = new Marine(3, 5);
    • marines[0]->show_status();
    • marines[1]->show_status();
    • std::cout << std::endl << "마린 1 이 마린 2 를 공격! " << std::endl;
    • marines[0]->be_attacked(marines[1]->attack());
    • marines[0]->show_status();
    • marines[1]->show_status(); delete marines[0];
    • delete marines[1]; }
  • -> 로 선언되는것은 포인터로 배열을 가리키기 때문에 

  • [정의] 소멸자(destructor) 는 객체가 생성될때 자동으로 호출되는 constructor처럼 객체가 사라질때 자동으로 호출되는 함수.
    ~(클래스의 이름)

    • 여기에서는 name = new char[strlen(marine_name)+1]; 이 생성자 안에서 호출되었는데 이를 소멸자에서  delete 를 통해 메모리 해제
    • Marine::Marine(int x, int y, const char* marine_name) { //마린의 생성자
        name = new char[strlen(marine_name) + 1];
        strcpy(name, marine_name);
        coord_x = x;
        coord_y = y;
        hp = 50;
        damage = 5;
        is_dead = false;
      }
    • 마린 소멸자
      Marine::~Marine() {
        std::cout << name << " 의 소멸자 호출 ! " << std::endl;
        if (name != NULL) {
          delete[] name; // 여기서 주목할점은 name이 char의 배열로 선언되었기 떄문에 delete[]를 꼭붙여줘야함
        }
      }​
       

  • [정의] 복사 생성자( copy constructor) 깊은 복사(deep copy)얕은 복사(shallow copy)로 이루어져있어 깊은 복사일때는 메모리를 할당 시키고 얕은 복사는 메모리 할당없이 복사로만 생성자 생성! 
    • 표준 정의 포맷은
      T(const T& a);​
       
    • [특성] const가 붙게 됨으로써 a의 인스턴스 변수들은 변하지 않아 오염을 방지해준다. 
    • // 포토캐논
      #include <string.h>
      #include <iostream>
      
      class Photon_Cannon {
        int hp, shield;
        int coord_x, coord_y;
        int damage;
      
       public:
        Photon_Cannon(int x, int y);
        Photon_Cannon(const Photon_Cannon& pc);
      
        void show_status();
      };
      Photon_Cannon::Photon_Cannon(const Photon_Cannon& pc) {
        std::cout << "복사 생성자 호출 !" << std::endl;
        hp = pc.hp;
        shield = pc.shield;
        coord_x = pc.coord_x;
        coord_y = pc.coord_y;
        damage = pc.damage;
      }
      Photon_Cannon::Photon_Cannon(int x, int y) {
        std::cout << "생성자 호출 !" << std::endl;
        hp = shield = 100;
        coord_x = x;
        coord_y = y;
        damage = 20;
      }
      void Photon_Cannon::show_status() {
        std::cout << "Photon Cannon " << std::endl;
        std::cout << " Location : ( " << coord_x << " , " << coord_y << " ) "
                  << std::endl;
        std::cout << " HP : " << hp << std::endl;
      }
      int main() {
        Photon_Cannon pc1(3, 3);  기본 생성자
        Photon_Cannon pc2(pc1);  복사 생성자
        Photon_Cannon pc3 = pc2; 이것 역시 복사 생성자
      
        pc1.show_status();
        pc2.show_status();
      }
    • 실행 결과
    • 생성자 호출 !
    • 복사 생성자 호출 !
    • 복사 생성자 호출 !
    • Photon Cannon Location : ( 3 , 3 )
    • HP : 100 Photon Cannon Location : ( 3 , 3 )
    • HP : 100
  • [정의] 디폴트 복사 생성자는 기존의 디폴트 생성자와 디폴트 소멸자가 아무것도 하지않는것에 반해 실제로 '복사'를 실행함. 즉, 일일이 복사생성자 써주지않아도 알아서 기본 복사 생성자를 만들어준다.

  • [특성] 복사 생성자의 한계점: 디폴트 복사 생성자는 얕은 복사 밖에 하지못하므로 깊은 복사가 필요하다면 유저가 직접 복사 생성자에 동적 메모리 선언을 해줘야함. 예를 들어 복사 생성자 없이 char를 new로 선언하고 이름을 붙여준후에 소멸자에서 delete로 기존 name 메모리를 해제하였을경우 같은 메모리가 2번 해제되는 상황이 발생해 에러가 뜬다. 이를 위해 복사 생성자를 직접 선언하고 이언에 char를 new로 선언한후에 해제가 된다면 각각 다른 메모리에서 해제가 되는것으로 에러방지.
  • 여기서 name이 복사 생성자안에서 동적 할당이 됨으로써 깊은복사가 이루어진다
    Photon_Cannon::Photon_Cannon(const Photon_Cannon &pc) {
      std::cout << "복사 생성자 호출! " << std::endl;
      hp = pc.hp;
      shield = pc.shield;
      coord_x = pc.coord_x;
      coord_y = pc.coord_y;
      damage = pc.damage;
    
      name = new char[strlen(pc.name) + 1];
      strcpy(name, pc.name);
    }

다시 한번 매우 중요한 개념들 상기 :)

댓글