이번 항목은 타입변환에 대한 내용.
코드를 통해 문제점 부터 파악해 보자.
#include <iostream>
using namespace std;
class AAA
{
int m_iNum1;
public:
AAA( int num1 = 0, int num2 = 1 ) // 단일 인자 생성자
{
m_iNum1 = num1 / num2;
}
operator double() const;
};
AAA::operator double() const
{
return static_cast<double>(m_iNum1);
}
void main()
{
AAA a( 3, 2 );
double d = 0.5 * a; // 아마 본래 의도는 이런 것이겠거니....
cout << d << endl;
// 그런데, 이게 왜 되는걸까?
cout << a << endl;
}
사실 코드만 보면 << 에 대한 연산자 오버로딩이 없기 때문에 안될거 같은데? 라고 생각하겠지만,
실제론 형변환이 이루어 지면서 출력이 된다.
이런 상황을 제거하기 위해, operator double가 아닌, 함수로써 변경을 하는 방법이 존재한다.
#include <iostream>
using namespace std;
class AAA
{
int m_iNum1;
public:
AAA( int num1 = 0, int num2 = 1 ) // 단일 인자 생성자
{
m_iNum1 = num1 / num2;
}
// operator double() const;
double asDouble() const
{
return static_cast<double>(m_iNum1);
}
};
//AAA::operator double() const
//{
// return static_cast<double>(m_iNum1);
//}
void main()
{
AAA a( 3, 2 );
// double d = 0.5 * a;
double d = 0.5 * a.asDouble();
cout << d << endl;
// 이젠 안된다.
// cout << a << endl;
// 복잡하다고 느낄 수 있지만,
// string str;
// str.c_str(); 을 생각해 보자
// 같은 이유다.
cout << a.asDouble() << endl;
}
다음 예제를 보자.
#include <iostream>
using namespace std;
template< class T >
class Array
{
public:
// 의도한 상황과 비슷한 연출을 위해
// 어느정도 만들다 보니 본래 클래스의 역활은 거의 없다;
// 단지 상황을 연출하기 위한 클래스 라고만 보자.
T m_nArr[10];
int m_nLow;
int m_nHight;
public:
Array(int lowBound, int highBound);
Array(int size);
T& operator[]( int index );
};
template< class T >
Array<T>::Array( int lowBound, int highBound )
{
m_nLow = lowBound;
m_nHight = highBound;
}
// 이번문제에 주 원인이 되는 생성자
template< class T >
Array<T>::Array( int size )
{
m_nLow = 0;
m_nHight = size;
}
template< class T >
T& Array<T>::operator[]( int index )
{
return m_nArr[index];
}
bool operator==( const Array<int>& lhs,
const Array<int>& rhs )
{
// 사실 여기서 무슨일을 하든 지금 상황과는 중요치 않다.
for( int i=0; i<10; ++i )
{
if( lhs.m_nArr[i] != rhs.m_nArr[i] )
return false;
}
return true;
}
void main()
{
Array<int> a(10);
Array<int> b(10);
for( int i=0; i<10; ++i )
{
// 이제 문제다.
// 내가 원한건 a[i] == b[i] 였고,
// 지금 상황은 컴파일이 되지 않아야 정상이다.
if( a == b[i] ) // 하지만 컴파일에서 문제 없음.
{
// 어떤 내용이 들어가 있는지는 현재로썬 중요치 않다
}
else
{
// 어떤 내용이 들어가 있는지는 현재로썬 중요치 않다
}
}
}
다음과 같이, 암시적 변환을 사용하게 되었을 때는 원하지 않는 결과에 도출하게 된다.
물론 a == b[i]가 의도한 코딩이라면 상관없을지도 모르겠지만,
현재 상황에서 저 코드에 문제가 있다는 것을 시력이 좋거나,
런타임 후에 확인하거나,,
둘중 하나일 것이다.
코드를 하나하나 따라가 보면,
a 의 생성자에 10번,
operator==에 10번 들른다.
그 부분에 대해서는 항목 19에서 다시 설명한다고 한다.
자 문제는 파악 했고, 이제 저런 상황에서 우리가 원하지 않는 암시적 변환을 막고 싶다고 하였을 때,
어떻게 해야 할까? 답은 생각보다 쉬웠다.
explicit
explicit Array(int size); // 바로 이것!!!!
이로써 이전에 if( a == b[i] ) 부분에서 컴파일 오류가 날것이다.
하지만 모든 문제가 해결된 것은 아니다.
비록 암시적 변환은 해결 됐지만,
명시적 타입 변환은 여전히 허용되는 것이다.
if( a == Array<int>(b[i]) ) // 문제는 없다.
if( a == static_cast< Array<int> >(b[i]) ) // 문제는 없는데,
if( a == (Array<int>b[i] ) // 알 수 없는 거부감이 드는 코드들이다.
현재로썬 저런 어색한 부분에 대한 해결책은 언급하고 있지 않지만,
명시적 타입 변환이라고 해도, 이전에 이야기 했듯이 지나친 형변환은 지양하자는 말이 떠오르는 코드다.
이번 타입 변환과는 별개로, 2번째 if문에 존재하는 > > 사이의 공백에 대해 언급을 하고 있는데,
저곳에 공백을 넣지 않으면 operator >> 를 호출 한다고 하니, 주의하도록 하자.
(필자는 그냥 무조건 공백을 주는 습관을 가지고 있으니 괜찮다!)
이 후에는 explicit 를 지원하지 않는 컴파일러에서의 해결 방법을 이야기 하고 있는데,
안봐도 되지만, 내용이 프록시 클래스 라는 것에 대해 미리 이야기 하고 싶었던것 같다.
한번 그냥 훑어 보는 정도로 넘어가면 될듯.
'프로그래밍 > More Effective C++' 카테고리의 다른 글
항목7. &&, ||, 혹은 , 연산자는 오버로딩 대상이 절대로 아니다. (0) | 2011.06.30 |
---|---|
항목6. 증가 및 감소 연산자의 전위 / 후위 형태를 반드시 구분하자. (0) | 2011.04.29 |
항목4. 쓸데 없는 기본 생성자는 그냥 두지 말자. (1) | 2011.04.25 |
항목3. 배열과 다형성은 같은 수준으로 놓고 볼 것이 아니다. (0) | 2011.04.25 |
항목2. 가능한 C++ 스타일의 캐스트를 즐겨 쓰자. (0) | 2011.04.22 |