리팩토링
리팩토링은 내부의 코드를 개선하는 하나의 방법론이자 솔루션을 뜻합니다.
리팩토링을 한마디로 말하자면, 나쁜 냄새가 나는 코드를 최적화하는 것입니다. 여기서 주의할 점은 런타임 시의 최적화가 아니라 프로그래밍을 할 때 버그 없는 프로그램을 만드는 것입니다. 외부 인터페이스는 그대로 두고 내부의 코드만을 개선하여 재사용과 가독성을 높이는 데 목적이 있습니다.
[ 리팩토링 시리즈 ]
[C++] 누구나 쉽게, 리팩토링(클린코드)-① 순수가상함수/추상클래스
[C++] 누구나 쉽게, 리팩토링(클린코드)-② 인터페이스
[C++] 누구나 쉽게, 리팩토링(클린코드)-③ 캐스팅(이번글)
이번시간에는 리팩토링을 진행하면서 캐스팅 개념에 대해 살펴보겠습니다.
3. 캐스팅
캐스팅이란 형변환을 뜻합니다. 잘못된 형변환은 오류를 불러올 수 있습니다.
1) char* pa = malloc(sizeof(char));
2) char* pb = (char*)malloc(sizeof(char));
위 두 문장은 C 및 C++ 환경에서 문제없이 동작할까요? 정답은 아래에서 공개합니다!
1) C에서는 잘 동작하며 C++에서는 타입에러! 발생!
2) C/C++에서 모두 잘 동작합니다
타입체크 기능
C언어 : 타입체크 기능 X, C++ : 타입체크 강화(에러발생)
C언어에서는 명시적으로 타입변환을 하지 않아도 컴파일러는 알아서 잘 짰거니 하고 개발자가 믿고 실행합니다. 논리적으로 잘못됐어도 에러를 내지 않으니 잘못 개발한 개발자는 월화수목금금금...😭 이와 달리, C++에서는 타입 체크가 강화되어 타입을 명시하지 않으면 컴파일 에러가 납니다. 따라서 C++에서는 타입을 정확히 명시해주어야합니다.
그러나(char*) 이런 식으로 명시적 캐스팅 연산자를 쓰는 건 냄새나는 코드입니다. 이렇게 명시를 통한 캐스팅은 컴파일러의 타입체크 기능을 꺼버리기 때문입니다.
♥ 컴파일러가 형변환시 에러를 체크하게끔 하라!!
하여 4가지 캐스팅에 대해 살펴보겠습니다.
1) static_cast
앞서 말했듯이 C++은 명시적인 형변환 연산자를 사용하면 안 됩니다. 이는 컴파일러의 타입 체크 기능을 끄게 하기 때문입니다. 대신 static_cast를 사용하여 이상적으로 형 변환을 합니다.
char *pc = static_cast<char*>(malloc(sizeof(char)));
static_cast는 인스턴스 되기 전에 컴파일 타임에 캐스팅됩니다. 컴파일러가 캐스팅을 책임지므로 아름답죠
2) reinterpret_cast
int x = 0x1;
char *p = &x;
x는 int형인데 char형 포인터를 사용할 경우(unreasonable error라고 표현합니다) C언어에선 문제없었지만, C++에서는 강력한 타입 체크 기능에 걸려(포인터의 타입이 다르기 때문에 )에러가 납니다.
빅엔디안인지 리틀 엔디안인지 확인해 보는 경우와 같이 의도적으로 비이상적인 코딩을 한 경우에도 C++에서는 에러를 내어 개발자가 의도적으로 사용하고 싶어도 쓰지 못하게 됩니다. 이는 컴파일러가 비이상적이라고 생각했기 때문입니다.
char *p = reinterpret_cast<char*>(&x);
이러한 경우에 의도적으로 비이상적인 형 변환을 하고자 한다면, reinterpret_cast 사용하면 됩니다. reinterpret_cast는 비이상적 형 변환을 지원합니다.
3) const_cast
const double PI = 3.14;
double* p = &PI;
const 상수를 사용하였으므로 논리적으로 변환이 되지 않도록 하는 것이 맞지만 그럼에도 불구하고 변환을 해야 할 때가 있습니다. 이럴 때 논리적 상수성을 깨려면 const_cast를 사용합니다.
double* p = const_cast<double*>(&PI);
** const_cast 캐스트를 한다는 건 const 상수 설계가 제대로 되지 않은 경우가 많습니다. 그러므로 가급적이면 안쓰는 게 좋습니다.
4) dynamic_cast
이번엔 1탄에서 사용한 Animal 클래스를 살펴봅시다. 간단하게 Animal 클래스를 설명하면, Animal 클래스를 상속받은 클래스들은 동물들이 우는 기능(cry())을 강제화하게끔 추상클래스로 구현하였고 Dog클래스는 Animal클래스의 자식클래스입니다.
업캐스팅Upcasting
22 라인의 Animal* p = &d; 는 Animal과 Dog의 타입이 달라 statc_cast 캐스팅하여 Animal *p = static_cast<Animal*>(&d); 로 써야하는 것 아닌가? 하겠지만,
♥ 업캐스팅시 상속 관계에 있을 때는 캐스팅 연산자 생략 가능합니다. 컴파일 시에 이미 두 클래스 간의 관계를 알고 있기 때문입니다. 따라서 static_cast를 쓰지 않아도 Animal* p = &d;로 표현 가능한 것이죠.
다운캐스팅Downcasting
그렇다면 다운 캐스팅은 어떠할까요? Animal을 Dog로 다운 캐스팅하는 Dog* pDog = p; 는 불가능합니다.
♥ Downcasting에서는 연산자를 생략할 수 없습니다. 부모의 타입을 자신의 타입으로, 안전한 다운 캐스팅을 하고 싶다면 dynamic_cast 연산자를 사용해야 합니다.
Dog* pDog = dynamic_cast<Dog*>(p);
이때 주의해야할 점은 dynamic_cast는 컴파일에 체크되지 않으므로 26~29라인처럼 런타임에 체크하게 합니다.
if( pDog != 0)
cout << "Dog" << endl;
else
cout << "!Dog" << endl;
정리
// 1) static_cast : 보편적인 캐스팅 시(명시적 형변환 안돼요)
char *pc = static_cast<char*>(malloc(sizeof(char)));
// 2) reinterpret_cast : 비이상적 캐스팅 시
int x = 0x1;
char *p = &x;
char *p = reinterpret_cast<char*>(&x);
// 3) const_cast : const 캐스팅
const double PI = 3.14;
double* p = &PI;
double* p = const_cast<double*>(&PI);
// 4) dynamic_cast : 다운캐스팅 시 사용
// Animal 클래스는 Dog 클래스의 부모 클래스일 때,
Dog d;
Animal* p = &d;
Dog* pDog = dynamic_cast<Dog*>(p);
1)static_cast : 이상적 캐스팅에 사용
2) reinterpret_cast : 비이상적 캐스팅에 사용
3) const_cast : 상수 변환에 사용
4) dynamic_cast : 다운캐스팅에 사용
이상 캐스트에 대하여 살펴보았습니다.
'컴퓨터공학과 > Programming' 카테고리의 다른 글
[C++] 객체지향 개념(객체/클래스/생성자) 및 헤더파일 분리 요약정리 (0) | 2020.04.13 |
---|---|
[C++] 누구나 쉽게, 리팩토링(클린코드)-④ 상속 (개념과 특징) (0) | 2020.04.13 |
[C++] 누구나 쉽게, 리팩토링(클린코드)-② 인터페이스 (1) | 2020.04.07 |
[C++] 함수에 관한 모든것(함수 특징/Call by value/call by reference/지역변수/전역변수/함수 오버로딩) (0) | 2020.04.05 |
[C++] 누구나 쉽게, 리팩토링(클린코드)-① 순수가상함수/추상클래스 (0) | 2020.03.25 |