본문 바로가기
Develop/C++

[C ++] 변환 생성자

by J-rain 2023. 2. 4.

'매개변수가 한 개인 생성자' 를 다른 말로 '변환 생성자' 라고도 한다. 문제는 이 변환 생성자가 은근슬쩍 호출되거나 불필요한 임시 객체를 만들어냄으로써 프로그램의 효율을 갉아먹는 원인이 된다는 것이다.

#include <iostream>
using namespace std;

// 제작자 코드
class Rain
{
  public:
  // 매개변수가 하나뿐인 생성자는 형변환이 가능하다.
  Rain(int Param) : Data(Param)
  {
    cout<< "Rain(int)"<< endl;

  }

  Rain(const Rain &rhs) : Data(rhs.Data)
  {
    cout << "Rain(const Rain &rhs)" << endl;

  }

  int GetData() const { return Data; }
  void SetData(int Param) { Data = Param; }

  private:
  int Data = 0;

};

// 사용자 코드
// 매개변수가 클래스 형식이며 변환 생성이 가능하다.
void TestFunc(Rain param)
{
  cout << "TestFunc(): " << param.GetData() <<endl;
}
int main(void)
{
  // int 자료형에서 Rain 형식으로 변환될 수 있다.
  TestFunc(5);

  return 0;
}

실행결과

TestFunc() 함수의 매개변수는 Rain 클래스 형식이다. 그리고 Rain 클래스는 int 자료형에 대한 변환 생성자 (Rain(int))를 제공한다. 그렇기 때문에 38번행의 TestFunc(5); 라는 코드를 작성할 수 있게 되었다.

그렇다면  소멸자를 추가하고 31번행의 매개변수를 참조형식으로 바꿔주게 되면 어떻게 될까?
#include <iostream>
using namespace std;

// 제작자 코드
class Rain
{
  public:
  // 매개변수가 하나뿐인 생성자는 형변환이 가능하다.
  Rain(int Param) : Data(Param)
  {
    cout<< "Rain(int)"<< endl;

  }

  Rain(const Rain &rhs) : Data(rhs.Data)
  {
    cout << "Rain(const Rain &rhs)" << endl;

  }
  ~Rain()
  {
    cout << "~Rain()" << endl;
  }
  int GetData() const { return Data; }
  void SetData(int Param) { Data = Param; }

  private:
  int Data = 0;

};

// 사용자 코드
// 매개변수가 클래스 형식이며 변환 생성이 가능하다.
void TestFunc(const Rain &param)
{
  cout << "TestFunc(): " << param.GetData() <<endl;
}
int main(void)
{ 
  cout << "시작" << endl;

  // 새로운 Rain 객체를 생성하고 참조를 전달한다.
  TestFunc(5);

  // 함수가 반환되면서 임시 객체는 소멸한다.
  cout << "끝" << endl;

  return 0;
}​

실행결과

결과를 보면 분명히 변환 생성자가 호출 됐음을 확인할 수 있다. 그런데 main()함수의 어디에도 Rain 클래스 객체를 선언하거나 동적으로 생성하는 코드가 전혀없다!  -> 컴파일러가 '알아서' 임시 객체를 생성한 후 이 임시 객체에 대한 참조가 TestFunc() 함수로 전달되도록 했다. 그리고 임시 객체는 TestFunc() 함수를 반환함과 동시에 소멸시켰다. 즉, TestFunc(5); 라는 코드가 실제로 TestFunc(Rain (5)); 라고 쓴것과 동일해진 것이다.

앞서 복사 생성자를 말하면서 강조했던 것은 매개변수로 사용할 거라면 무조건 참조 형식을 사용하라 했다. -> https://j-ra1n.tistory.com/entry/%EB%B3%B5%EC%82%AC-%EC%83%9D%EC%84%B1%EC%9E%90

이제는 하나 더 붙여서 묵시적 변환 생성자를 지원하는 클래스인지 꼭 확인해야한다. 내부적으로 메모리를 동적 할당하는 등 덩치가 제법 나가는 객체라면 묵시적 변환 생성자가 사용자 모르게 호출될 가능성을 차단하는 것이 바람직하다!!

// 제작자 코드
class Rain
{
 public:
 
  // 매개변수가 하나뿐인 생성자는 형변환이 가능하다.
  // 하지만 묵시적으로 불가능하도록 차단한다.
  explicit Rain(int Param) : Data(Param)
  {
    cout << "Rain(int)" << endl;
  }
  
  ....

이 코드를 보면 explicit 예약어를 확인할 수 있다. 이것을 사용하면 임시 객체가 생성되지 못하게 막는다.

 

따라서 TestFunc(5); 같은 코드는 허용되지 않는다. 무조건 임시 객체가 생성된다는 사실이 명확하게 드러나는 TestFunc(Rain (5)); 형태로 작성해야한다.

 

 

'Develop > C++' 카테고리의 다른 글

[C++] cin, getline 함수  (0) 2023.02.20
[C ++] r-value 참조  (0) 2023.02.20
[C ++] 복사 생성자  (0) 2023.02.04
[C ++] 상수형 메소드  (0) 2023.02.03
[C ++] 생성자와 소멸자  (0) 2023.02.03

댓글