프로그래밍 언어/C & C++ 정리

생성자를 통한 자동 형변환, 변환 함수

게임 개발 2023. 3. 2. 12:41

생성자를 통한 자동 형변환

생성자를 자동 데이터형 변환 함수로 동작하게 하는 것은 멋진 기능처럼 보이지만,

프로그래머들이 C++를 많이 경험하게 되면,

자동 데이터형 변환이 항상 바람직한 것은 아니라는 것을 알게 된다.

되려 예상치도 못한 부작용을 일으킬 수도 있다는 프로그래머들은 알게 될 경우도 있다.

 

그래서,

최신 C++ 시스템에는 자동 데이터형 변환을 못하게 하는 explicit라는 새로운 키워드가 추가되었다.

 

explicit Stonewt (double lbs);		// 암시적 데이터형 변환을 허용하지 않는다.

 

위 예제와 같이 생성자에 explicit 앞의 예와 같은 암시적 데이터형 변환을 못하게 막는다.

그러나 다음과 같은 명시적 데이터형 강제 변환은 여전히 허용한다.

 

explicit 는 암시적 형변환을 막는 용도로 쓰인다.

모든 형변환을 막는 것이 아닌,

암시적 형변환의 사용 만을 막고,

다음과 같은 명시적 형변환은 허용한다.

 

Stonewt myCat;		// Stonewt 객체를 생성한다
myCat = 19.6;		// Stonewt (double)이 explicit로 선언되면 틀리다
myCat = Stonewt(19.6);		// 맞다, 명시적 데이터 형 변환
myCat = (Stonewt) 19.6;		// 맞다, 명시적 데이터형 변환의 옛날 형식

즉 이처럼 explicit는 명시적 데이터형 변환만 할 수 있다.

그렇지 않은 경우(= explicit 키워드를 추가하지 않은 경우) 에는

암시적 데이터형 변환도 할 수 있다.

 

그렇다면 컴파일러는 Stonewt(double) 함수를 언제 사용할까?

키워드 explicit가 선언에 사용된 경우에, Stonewt (double) 은 명시적 데이터형 변환에만 사용된다.

그렇지 않은 경우(= explicit 키워드를 추가하지 않은 경우)에는 다음과 같은 암시적 데이터형 변환에도 사용된다.

 

  • Stonewt 객체를 double형 값으로 초기화할 때
  • Stonewt 객체에 double형 값을 대입할 때
  • Stonewt형 매개변수를 기대하는 함수에 double형 값을 전달할 때
  • Stonewt형 값을 리턴하도록 선언된 함수가 double형 값을 리턴하려고 시도할 때
  • 앞의 네 상황에서 double형 대신 모호하지 않게 double형으로 변환할 수 있는 내장 데이터형을 사용할 때

double형으로 변환할 수 있는 내장 데이터형을 사용할 때

 

마지막 다섯 번째 상황을 좀 더 자세히 살펴보자면,

함수 원형에 비추어 매개변수를 일치시키는 과정이,

Stonewt(double) 생성자가 다른 수치 데이터형에 대해서도 변환을 처리할 수 있도록 해 준다.

즉, 다음과 같은 두 구문은 int형을 double형으로 먼저 변환한 후에

Stonewt(double) 생성자를 사용함으로써 바르게 동작한다.

 

Stonewt Jumbo(7000);		// int를 double로 변환, Stonewt(double) 사용
Jumbo = 7300				// int를 double로 변환, Stonewt(double) 사용

 

그러나 이 2단계 변환 과정은 double형으로의 변환이 모호하지 않은 경우에만 동작한다.

즉, 그 클래스가 Stonewt(long) 생성자도 따로 정의해 두었다면,

컴파일러는 이들 구문의 수행을 거부할 것이다.

왜냐하면 int형은 long형으로 또는 double형으로 어느 쪽으로도

변환할 수 있으므로 호출이 모호해지기 때문이다.

 

그렇다면 컴파일러는 이처럼 int형을 double형으로 먼저 변환한 후,

Stonewt(double)생성자를 사용하여

그것을 사용자 정의 자료형인 Stonewt객체로 변환한다.

 

변환 함수

 

하나의 수를 Stonewt 객체로 변환하는 것이 가능하다면

이 과정을 반대로 할 수 없을까?

즉, 다음과 같이 Stonewt 객체를 double형 값으로 변환할 수 있을까?

 

Stonewt wolfe(285.7);
double host = wolfe;			// 이러한 형변환이 가능할까?

 

이러한 형변환은 가능하다.

그러나 위의 경우처럼 생성자를 사용하지는 않고

생성자는 어떤 데이터형을 클래스형으로 변환하는 것만을 허용한다.

이 과정을 반대로 하기 위해서는,

변환 함수(conversion function)라고 부르는 특별한 형태의 C++ 연산자 함수를 사용해야 한다.

 

변환 함수는 사용자 정의 강제 데이터형 변환이다.

따라서 일반적인 강제 데이터형 변환 (type cast) 처럼 사용하면 된다.

Stonewt 를 double로 변환하는 함수를 정의했다면, 다음과 같은 변환을 사용할 수 있다.

 

Stonewt wolfe (285.7)
double host = double(wolfe);		// 문법 #1
double thinker = (double)wolfe;		// 문법 #2

 

또는 이 문제를 컴파일러가 스스로 처리하게 할 수 있다.

 

Stonewt wells (20, 3);
double star = wells;		// 변환 함수를 암시적으로 사용한다.

 

오른쪽이 Stonewt 형이고 왼쪽이 double형이라는 것을 인식한 컴파일러는,

사용자가 이 문제를 해결할 수 있는 변환 함수를 정의했는지 찾아본다.

(만약 그러한 정의를 찾을 수 없다면,

컴파일러는 Stonewt형을 double형에 대입할 수 없다는 에러 메시지를 내보낸다.)

 

그렇다면 변환 함수는 어떻게 작성해야 할까?

typeName형으로 변환하려면, 변환 함수를 다음과 같은 형식으로 사용한다.

 

operator typeName();

 

다음과 같은 사항을 기억해 두자.

 

  • 변환 함수는 클래스의 메서드여야 한다.
  • 변환 함수는 리턴형을 가지면 안 된다.
  • 변환 함수는 매개변수를 가지면 안 된다.

 

예를 들어, double형으로 변환하는 함수 원형이 다음과 같을 것이다.

 

operator double();

 

typeName 부분이 ( 이 경우에는 typeName이 double이다) 어떤 데이터형으로 변환할 것인지 알려준다.

그래서 리턴형이 필요 없다. 그 함수가 클래스 메서드라는 사실은,

그것이 특정 클래스 객체에 의해 호출되어야 한다는 것을 의미한다.

객체가 그 함수에게 변환할 값을 알려 준다. 그러므로 변환 함수는 매개변수를 필요로 하지 않는다.

 

Stonewt 객체를 int형과 double형으로 각각 변환하는 함수를 추가하려면,

클래스 선언에 다음과 같은 원형을 추가해야 한다.

 

operator int() const;
operator double() const;

// 뒤에 const를 붙이는 편이 훨씬 안전한 방법이다
// 이유는 나도 모른다 이따 찾아보기

 

만약 여러 개의 형변환 operator 함수를 사용하였다면,

명시적 형변환을 사용하는 것이 깔끔하고 좋다.

 

long gone = (double) poppins;	// double 변환 함수를 사용한다
long gone = int (poppins);		// int 변환 함수를 사용한다

 

첫 번째 구문운 poppins를 double형 값으로 변환한 후,

gone에 대압하기 위해 long형 값으로 변환한다.

마찬가지로 두 번째 구문은 poppins를 int형 값으로 변환한 후,

다시 long형 값으로 변환한다.

 

변환 생성자와 마찬가지로, 변환 함수도 좋은 점도 있고, 나쁜 점도 있다. 

암시적 변환을 자동으로 수행하는 함수의 문제점은,

원하지 않을 때에도 그러한 변환을 수행할 수 있다는 것이다.

 

이 때문에 암시적 변환은 신중하게 사용해야 한다.

명시적으로만 호출할 수 있는 함수를 사용하는 것이 가장 바람직하다.

 

이 때문에 위에서 배운 explicit 키워드와 변환 함수와 어울려 사용하는 것이 유용하다.

 

class Stonewt
{
. . .
// 변환 함수
	explicit operator int() const;
    	explicit operator double() const;
};

 

이러한 선언이 있다면, 프로그래머(= 사용자)는 연산자들을 통해서 형변환을 할 수 있다.

 

 

(명시적으로 호출할 경우에만) 동일한 작업을 수행하는 비변환 함수로 변환 함수를

대체하면 된다. 즉, 다음과 같은 것을

 

Stonewt::operator int() { return int (pounds + 5.0) }

을 다음과 같이 대체할 수 있다.

 

int Stonewt::Ston_to_Int() { return int (pounds + 5.0); }

 

이것은 다음과 같은 구문을 허용하지 않는다. (= 암시적 형변환)

 

int plb = poppins;

 

그러나 꼭 변환이 필요하다면 다음과 같은 구문은 허용한다.

 

int plb = poppnis.Stone_to_Int();

 

'프로그래밍 언어 > C & C++ 정리' 카테고리의 다른 글

C++ STL) deque란?  (0) 2023.04.10
OBB 충돌  (0) 2023.04.06
분할 컴파일  (0) 2023.02.21
L-Value R-Value  (0) 2023.02.20
C++ 파일 입출력  (0) 2023.02.11