[C++] 소멸자, 복사 생성자, 얕은 복사, 깊은 복사

·2023년 8월 18일
0

C++

목록 보기
5/17
post-custom-banner

소멸자

~클래스의 이름();
소멸자는 생성자와 달리 인자를 아무것도 가지지 않음 -> 즉, 오버로딩이 되지 않음

Marine::~Marine() 
{
  std::cout << name << " 의 소멸자 호출 ! " << std::endl;
  
  // 동적으로 값이 할당된 경우 제거
  if (name != NULL) 
  {
    delete[] name;
  }
}

복사 생성자

T(const T& a);
다른 T의 객체 a를 상수 레퍼런스로 받음
a는 cont 이므로 복사 생성자 내부에서 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

// 인자 2개 받는 생성자 호출
Photon_Cannon pc1(3, 3); 

// 복사 생성자 호출
Photon_Cannon pc2(pc1);
Photon_Cannon pc3 = pc2; // 컴파일러는 해당 문장을 Photon_Cannon pc3(pc2); 이렇게 해석함

// 주의
Photon_Cannon pc3;
pc3 = pc2; // 이건 복사 생성자가 호출된 케이스가 아님 -> 복사 생성자는 오직 '생성' 시에만 호출됨

디폴트 복사 생성자

위 내용에서 복사 생성자를 지우고 실행해도 이전과 같은 결과가 나옴
-> 디폴트 복사 생성자는 기존의 디폴트 생성자, 소멸자와 달리 실제로 '복사'를 해 줌

Photon_Cannon의 복사 생성자의 내용을 추정해 본다면 아래와 같을 거임

Photon_Cannon::Photon_Cannon(const Photon_Cannon& pc) 
{
  hp = pc.hp;
  shield = pc.shield;
  coord_x = pc.coord_x;
  coord_y = pc.coord_y;
  damage = pc.damage;
}

디폴트 복사 생성자의 한계

// 디폴트 복사 생성자의 한계
#include <string.h>
#include <iostream>

class Photon_Cannon 
{
  int hp, shield;
  int coord_x, coord_y;
  int damage;

  char *name;

 public:
  Photon_Cannon(int x, int y);
  Photon_Cannon(int x, int y, const char *cannon_name);
  ~Photon_Cannon();

  void show_status();
};

Photon_Cannon::Photon_Cannon(int x, int y) 
{
  hp = shield = 100;
  coord_x = x;
  coord_y = y;
  damage = 20;

  name = NULL;
}

Photon_Cannon::Photon_Cannon(int x, int y, const char *cannon_name) 
{
  hp = shield = 100;
  coord_x = x;
  coord_y = y;
  damage = 20;

  name = new char[strlen(cannon_name) + 1];
  strcpy(name, cannon_name);
}

Photon_Cannon::~Photon_Cannon() 
{
  // 0 이 아닌 값은 if 문에서 true 로 처리되므로
  // 0 인가 아닌가를 비교할 때 그냥 if(name) 하면
  // if(name != 0) 과 동일한 의미를 가질 수 있다.

  // 참고로 if 문 다음에 문장이 1 개만 온다면
  // 중괄호를 생략 가능하다.

  if (name) delete[] name;
}

void Photon_Cannon::show_status() 
{
  std::cout << "Photon Cannon :: " << name << std::endl;
  std::cout << " Location : ( " << coord_x << " , " << coord_y << " ) "
            << std::endl;
  std::cout << " HP : " << hp << std::endl;
}

int main() 
{
  Photon_Cannon pc1(3, 3, "Cannon");
  Photon_Cannon pc2 = pc1;

  pc1.show_status();
  pc2.show_status();
}

컴파일 후 실행하면 오류 발생

pc1과 pc2는 모두 같은 변수를 가지게 됨
name이란 두 개의 포인터가 같은 값을 가짐 -> 같은 주소를 가리킴
이 경우 하나가 소멸되면서 name이 삭제되면 다른 하나는 댕글링 포인터를 가지게 됨
-> 한 번 해제된 메모리는 다시 해제될 수 없음 (접근한 것 자체만으로도 해도 오류)

디폴트 복사 생성자의 한계 해결 방법

위와 같은 경우를 얕은 복사라고 함
컴파일러가 생성하는 디폴트 복사 생성자는 얕은 복사만 할 수 있으므로
깊은 복사(name을 그대로 복사하지 말고 따로 다른 메모리에 동적 할당을 해서 내용만 복사하는 것)가 필요한 경우 사용자가 직접 복사 생성자를 만들어야 함

// 깊은 복사 예제
#include <string.h>
#include <iostream>

class Photon_Cannon 
{
  int hp, shield;
  int coord_x, coord_y;
  int damage;

  char *name;

 public:
  Photon_Cannon(int x, int y);
  Photon_Cannon(int x, int y, const char *cannon_name);
  Photon_Cannon(const Photon_Cannon &pc);
  ~Photon_Cannon();

  void show_status();
};

Photon_Cannon::Photon_Cannon(int x, int y) 
{
  hp = shield = 100;
  coord_x = x;
  coord_y = y;
  damage = 20;

  name = NULL;
}

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

Photon_Cannon::Photon_Cannon(int x, int y, const char *cannon_name) 
{
  hp = shield = 100;
  coord_x = x;
  coord_y = y;
  damage = 20;

  name = new char[strlen(cannon_name) + 1];
  strcpy(name, cannon_name);
}

Photon_Cannon::~Photon_Cannon() 
{
  if (name) delete[] name;
}

void Photon_Cannon::show_status() 
{
  std::cout << "Photon Cannon :: " << name << std::endl;
  std::cout << " Location : ( " << coord_x << " , " << coord_y << " ) "
            << std::endl;
  std::cout << " HP : " << hp << std::endl;
}

int main() 
{
  Photon_Cannon pc1(3, 3, "Cannon");
  Photon_Cannon pc2 = pc1;

  pc1.show_status();
  pc2.show_status();
}

실행 결과
복사 생성자 호출!
Photon Cannon :: Cannon
Location : ( 3 , 3 )
HP : 100
Photon Cannon :: Cannon
Location : ( 3 , 3 )
HP : 100

post-custom-banner

0개의 댓글