TIL 2024/8/06

Sung Joo Lee·2024년 8월 6일

복사 생성자 ( 중요 )


  • 객체가 복사될 때는, 기존 객체를 기반으로 새로운 객체가 생성됨

  • 객체가 복사되는 경우

    1. 객체를 pass by value 방식으로 함수의 매개변수로 전달할 대

      #include <iostream>
      
      void displayPlayer(Player p)
      {
      
      }
      
      int main()
      {
      
      	Player hero{ 0,0,1 };
      
      	displayPlayer(hero);
      
      	return 0;
      }
    2. 함수에서 value의 형태로 결과를 반환할 때

      #include <iostream>
      
      Player createSuperPlayer()
      {
      	Player anEnemy{ 1,1,1 };
      	return anEnemy;//반환
      }
      
      int main()
      {
      
      	Player enemy;
      	enemy = createSuperPlayer();
      
      	return 0;
      }
    3. 기존 객체를 기반으로 새로운 객체를 생성할 때

      // 1
      Player hero {1,1,1};
      
      Player anotherHero = hero;//복사 생성자 호출
      Player anotherHero {hero};// 객체를 생성할 때 hero를 복사해서 만들어라 
      													// 즉, 이건 동일한 의미
      
      ////////////////////////////////
      
      //2
      Player another;
      another = hero;// 대입 연산자 호출 됨
    • 위의 1,2의 방식은 완전히 다른 방식이다
      • 객체를 만들면서 대입하면 복사 생성자 호출
      • 생성하고 대입하면 대입 연산자 호출
  • 객체가 어떤 방식으로 복사될 지 정의해 주어야 함

    • 사용자가 구현하지 않은 경우 컴파일러에서 자동으로 복사 생성자를 정의
  • 왜 중요한가?

    • 복사 과정에서 문제가 발생할 수 있음( 포인터가 존재하는 경우 )
    • 복사 비용에 대한 인식 ( 클래스는 많은 데이터를 포함할 수 있음 )

자동 생성되는 복사 생성자

  • 사용자가 복사 생성자를 구현하지 않으면 , 기본 복사 생성자가 사용

  • 멤버 변수들의 값을 복사하여 대입하는 방식

  • 포인터 타입의 멤버 변수가 존재할 때는 주의!

    • 기본 복사 생성자는 포인터 타입의 변수 또한 복사하여 대입됨
    • 즉, 포인터가 가리키는 데이터의 복사가 아닌 포인터 주소 값의 복사
  • 얕은 복사(shallow copy) VS 깊은 복사(deep copy)

복사 생성자 선언

  • 동일한 타입의 const 참조자가 인자인 생성자
    • 참조자를 이용하여 값을 가져온다.

Type :: Type ( const Type& source);

예시

#include <iostream>

class Player
{
public:
	Player(int hp, int xp)
		:hp{ hp }, xp{ xp }
	{
		std::cout << "생성자 호출됨" << std::endl;//객체 생성시 body의 내용 출력
	}

	//복사 생성자

	Player(const Player& source) {
		std::cout << "복사 생성자 호출 됨" << std::endl;
	}

	void Print()
	{
		std::cout << hp << " " << xp << std::endl;
	}
private:
	int hp;
	int xp;

};

void PrintInformation(Player p) {
	p.Print();
}

int main()
{

	Player p{ 10,2 };//객체 생성
	PrintInformation(p);// 객체 p를 PrintInformation의 매개변수로 복사해야 함
						// 복사 생성자 호출 됨

	return 0;
}
  • 이때 주의 할 점
    • 위와 깉은 기본 복사 생성자와 같이 아무 것도 하지 않으면 PrintInformation의 매개변수 p가 텅 비어있는게 정상이다, 하지만 요즘 컴파일러에서 알아서 값을 넣어주는 경우도 있다.

얕은 복사 생성자의 구현

  • 자동 생성된 복사 생성자의 행동

생성자 초기화 리스트를 사용한 방법

Player(const Player& other)

      : x { other.x} , y { other.y } , speed{ other.speed }

{

}

대입을 사용한 방법

Player( const Player& other)
{
x = other.x ;
y = other.y;
speed = other.speed;
}

실제 자동 생성된 복사 생성자의 모습

Player(const Player& other)
{
				memcpy(this, &other, sizeof(other));
}

예시

위에서 말한 매개변수 p에 제대로 값이 들어가 있음

#include <iostream>

class Player
{
public:
	Player(int hp, int xp)
		:hp{ hp }, xp{ xp }
	{
		std::cout << "생성자 호출됨" << std::endl;//객체 생성시 body의 내용 출력
	}

	//복사 생성자

	Player(const Player& other)
		:hp{ other.hp } , xp{ other.xp }
	{
		std::cout << "복사 생성자 호출 됨" << std::endl;
	}

	void Print()
	{
		std::cout << hp << " " << xp << std::endl;
	}
private:
	int hp;
	int xp;

};

void PrintInformation(Player p)
{
	p.Print();
}

int main()
{

	Player p{ 10,2 };//객체 생성
	PrintInformation(p);// 객체 p를 PrintInformation의 매개변수로 복사해야 함
						// 복사 생성자 호출 됨

	return 0;
}
profile
개발로그

0개의 댓글