본문 바로가기
기술자료/C C++

[賢彬] 변환 연산자들(Conversion Operators)

by 알 수 없는 사용자 2009. 8. 12.
728x90
반응형

1.4 변환 연산자들(Conversion Operators)

C++ 및 C 코드가 함께 사용되는 것을 보는 것은 흔한 일이다. 예를 들면, 원래 C로 쓰여졌던 legacy 시스템들은 객체 지향 인터페이스에 의해 싸여질(wrap) 수 있다. 그러한 이중언어(dual language)를 사용하는 시스템들은 종종 동시에 이중적인 인터페이스를 지원할 필요가 있다 - 하나는 객체 지향환경을 제공하는 것이고 또 다른 것은 C 환경을 제공하는 것이다. 특정한 수치적 실재들(numeric entities) - 복소수 및 이진수와 같은 -과 nibbles을 구현하는 클래스들 또한 기본 타입들과의 더 부드러운 상호작용이 가능하도록 변환 연산자들(conversion operators)을 사용하는 경향이 있다.

문자열은 이중적인 인터페이스를 필요로 하는 고전적인 예이다. string 객체는 오직 NULL로 종결된 문자 배열들만을 지원하는 문맥에서 사용되어야 할 것이다. 예를 들면,

class Mystring
{
private:
       char *s;
       int size;
public:
       Mystring(const char *);
       Mystring();
       //...
};

#include <cstring>
#include "Mystring.h"
using namespace std;
int main()
{
       Mystring str("hello world");
       int n = strcmp(str, "Hello");   // 컴파일 시간 에러
                                       // str은 const char * 형이 아니다
        return 0;
}

C++은 그러한 경우들을 위하여 형 변환의 자동적인 수단들을 제공한다. 변환 연산자는 사용자 정의된 타입캐스팅 연산자로서 생각될 수 있다; 그것은 특정한 타입을 요구하는 문맥에서 그것의 객체를 다른 타입으로 변환한다. 변환은 자동적으로 이루어진다. 예를 들면,

class Mystring   // now with conversion operator
{
private:
       char *s;
       int size;
public:
       Mystring();
       operator const char * (){ return s; }  // Mystring을 C-스트링으로 변환
       //...
};

int n = strcmp(str, "Hello");   // OK, str의 const char*로의 자동적인 변환

변환 연산자들은 두 가지에서 보통의 오버로드된 연산자들과 다르다. 첫째, 변환 연산자는 리턴값(심지어는 void 조차도)을 갖지 않는다. 리턴값은 연산자의 이름으로부터 연역된다. 둘째, 변환 연산자는 어떤 arguments를 취하지 않는다. 변환 연산자들은 기본적이며 사용자-정의된 것과 유사한 어떤 주어진 타입으로 변환될 수 있다:

struct DateRep          // legacy C code
{
       char day;
       char month;
       short year;
};

class Date // object-oriented wrapper
{
private:
       DateRep dr;
public:
       operator DateRep () const{ return dr;} // DateRep로의 자동적인 변환
};

extern "C" int transmit_date(DateRep);  // C-기반의 통신 API 함수
int main()
{
       Date d; 
       //... d를 사용

       // date 객체를 원격 클라이언트에게 이진 스트림으로 전송
       int ret_stat = transmit_date;   // legacy 통신 API 사용
       return 0;
}

4.1 표준 대 사용자 정의 변환들(Standard Versus User-Defined Conversions)

사용자 정의 변환과 표준변환과의 상호작용은 바람직하지 않은 놀라움들과 부수적인 효과들을 초래할 수 있으므로 주의깊게 사용되어야만 한다. 다음의 구체적인 예를 조사하자. 하나의 argument를 취하는 비명시적(non-explicit) 생성자는 또한 그것의 argument를 이 클래스의 객체로 캐스트하는 변환 연산자이다. 컴파일러가 오버로드된 함수 호출을 분석해야 할 때, 그것은 표준적인 것들에 덧붙여서 그러한 사용자 정의 변환들을 고려한다. 예를 들어,

class Numeric
{
private:
       float f;
public:
       Numeric(float ff) : f(ff){} // 생성자는 또한 float-to-Numeric 변환 연산자
};

void f(Numeric);
Numeric num(0.05);
f(5.f);                     // OK, void f(Numeric)를 호출.
                           // Numeric의 생성자는 argument를 Numeric 객체로 변환

나중의 단계에서 f()의 또 다른 오버로드된 버전을 추가한다고 가정하자.

void f (double);

이제 동일한 함수 호출은 다르게 resolve한다.

f(5.f);                 // 이제는 f(Numeric)이 아니라 f(double)를 호출한다

이것은 float가 오버로드된 함수 signature와 일치하도록 하기 위하여 자동적으로 double로 promote되기 때문이다. 이것이 표준적인 형 변환(type conversion)이다. 다른 한편으로, float의 Numeric으로의 변환은 사용자-정의된 변환이다. 사용자-정의된 변환들은 오버로드 resolution에서 표준변환들 보다 더 낮게 분류된다; 결과적으로 함수 호출은 다르게 변한다.

이러한 현상과 다른 것들 때문에 변환 연산자들은 심하게 비판받아왔다. 몇몇 프로그래밍 교육기관들은 그것들 모두의 사용을 금지한다. 그러나, 앞으로 보게 되듯이 변환연산자들은 이중적인 인터페이스들(dual interfaces) 사이에 교량역할을 하는 가치가 있으며 때로는 피할 수 없는 도구이다.

728x90