== 기본자료형, 클래스, 구조체 등등 자료형을 바꿔준다.
자료형을 변환해줄 수 있는게 가능한 케이스가 3가지로 존재한다.
1-1.const_cast는 무엇인가?
const는 우리가 변수를 상수처럼 만드는것을 의미함
cast는 변환해준다는 뜻으로 즉 "상수를 변환해준다"와 같다.
우리가 코드를 짤 때 안전상으로 절대 안바뀔 변수에 const를 붙여 상수화를 시켜주는데 항상 계획은 변동사항이 있다보니 추가 기능을 요구하는 경우에 상수화 시킨 것들 중에 하나만 상수화를 시키지말자는 요구 사항이 생길때가 있다.그때 const를 빼주는 함수가 const_cast 함수다.
-> const라는게 애초에 절대 안건드린다는 취지로 쓰다보니 이 상황과 모순이여서 회사마다 다르지만 거의 사용하진않는다고한다.(문법적 차원에서 숙지)
이런 경우가 생긴다면 오히려 코드 설계를 바꾸는게 더 효율적이라고 한다.
1-2.const_cast 예제
void main()
{
char str[]="string";
const char c1 = str;
char c2;
c2 = const_cast<char * >(c1);
c2[0] = 'a';
printf("%s\n",c2);
}
한 줄씩 해석하자면
a.char str[]="string";
-> 배열 str을 선언하면서 문자열을 넣어줘 초기화시킴
(대괄호가 공란일 경우 문자의크기에 맞춰 크기가 넣어짐)
b.const char * c1 = str;
-> c1은 포인터 변수여서 주소값이 들어가야한다.str은 배열의이름이다보니 주소값을 지니고 있고 c1은 str이라는 배열을 가리키게된다.
-> 상수화가 된 케릭터형 포인터 변수 c1 을 str배열을 가리켜 초기화시킴
c1은 변하지않는 상수화된 변수여서 값이 정해져있어야만 상수화가 된다.그 말은 const도 레퍼런스 변수이기에 생성과 초기화가 동시에 되어야한다.
(const는 붙여짐과 동시에 바로 옆에 =으로 초기화시켜줘야된다.아랫줄에 초기화를 하면 안된다.)
c.char * c2;
-> 케릭터 포인터 변수 c2를 선언
d.c2 = const_cast<char * >(c1);
-> 여기서 const_cast 함수가 나오는데
구조는 const_cast< 원하는 자료형 >(바꿔줄 인자);
괄호 안에 인자(해당 바꿀 변수)를 넣어 꺽쇠 안에 있는 자료형으로 바꿔주는것
인자로 c1을 넣어주면 c1의 자료형인 const char 가 const를 제거한 char 로 바뀌는것이다.
그런고로 char * c1 = str;이 되고 c1이 가리키는 str이 c2에 넣어진다.
그래서 c2도 str을 가리키게된다.
e.c2[0] = 'a';
-> c2는 애초에 상수도 아니였고 const도 없기에 값이 변경이 가능하다.그래서 str에 넣어준 문자열에 접근해서 값을 변경하는게 가능하다.
string의 0번째 원소 값인 's'를 'a'로 바꿔준다.
f. printf("%s\n",c2);
-> atring이 출력된다.
-> 바로 이렇게 const를 제거 혹은 상수화를 해제하고싶을때 컨스트 케스트란 함수를 쓴다.
2-1. static_cast 함수란 무엇인가?
명시적 형 변환을 위한 캐스트 연산자
== 강제적으로 자료형을 바꿔줄때
2-2. 예시
class super
{
};
class sub : public super //부모로부터 자식이 상속
{
};
int main()
{
sub ss = static_cast<sub >(new super());
}
//자식 포인터 변수에다가 new super(부모 객체를 동적할당 해온것)의 자료형을 sub * 로 static_cast해서 넣어주겠다.
인자 자리에 들어가는 new super()는 현제 변수의 이름이 아니라 힙영역의 이름없는 메모리며 접근할 수 있는 방법은 주소값 밖에 없기에 주소값 가지고 컨트롤해야된다.그런고로 그 주소값을 가리키는 자료형 포인터 변수가 인자에 들어가는 것
동적할당하면 리턴값으로 주소값이 나온다.그리고 주소값을 받아온다는 것은 곧 포인터 변수(암기)
이 super라는 부모객체의 포인터 변수가 되는것이다.이걸 강제로 자식클래스인 sub * 로 자료형을 바꿔주겠다는것이다.
슈퍼라는 부모객체를 생성했기 때문에 원래는 super 포인터인데 이 포인터가 sub 포인터로 강제로 바뀌게된다.그리고 그 포인터변수가 가리키는 값이 즉 sub 포인터가 가리키는 주소값이 ss에 들어가게된다.
2-3.부모객체의 주소값을 자식객체에다가 바로 넣어주면안되는건가?
-> 직접 그렇게 하면 에러가 난다.
-> 이유는 포인터변수는 자료형을 맞춰줘야하는데 이때 예외사항으로 부모 포인터가 자식 객체를 가리킬때는 자료형이 달라도 가능했었다.근데 위같은 경우는 반대로 자식 포인터가 부모 포인터를 가리키는 형태다 보니깐 안돼서 (어려우면 이렇게 생각 sub * ss = new super() ) static_cast을 써줌으로서 자식과 똑같은 자료형으로 맞춰준것이다.자식 포인터가 자식 객체 가리키는건 가능
2-4.이런식으로 사용하는 경우는 드물고 보통은 일반적인 자료형을 바꿀때 static_cast 함수를 쓴다.
예시)
float f라는 실수형 변수가 있는데
float f = 5.6f라고 초기화 함
(5.6에 f를 붙여준 이유는 float라는걸 표시해주기위해서다. f를 안붙히면 double형으로 들어가버린다.)
내가 만약 int num에 변수 f를 넣어주면 출력했을땐 정수형이기때문에 5만 나온다.
이때 이건 c언어 특성상 0.6을 버리고 5만 나오게끔 하는거고 원래 정석 코드는
int num = static_cast(f);이다.
2-5.이처럼 static_cast는 강제로 자료형을 바꿔줄때 쓰인다.
3-1.dynamic_cast 함수는 무엇인가?
(다형성과 상속의 완벽한 이해가 전재되야 활용가능)
-> static_cast의 연한맛(강제여서 에러 안남)
-> casting 할 수 있으면하게하고 못하면 에러방지차 리턴값으로 null 값을 반환한다.
(빌드 오류,런타임 오류 등 에러나면 프로그램이 꺼지는데 그걸 방지)
-> 상속받은 클래스인 경우에만 사용가능하다.
3-2
예시)
class super
{
};
class sub : public super //부모로부터 자식이 상속
{
};
int main()
{
sub ss = dynamic_cast<sub >(new super());
}
아까처럼 함수명만 다르고 자식의 포인터로 부모의객체를 가리킬려고하는데 그걸 자식으로 바꿔줘하는 상태다.
-> 이것은 실행이 불가능하다.왜냐면 부모가 자식한테 물려주는건 되지만 자식이 부모한테 물려주는건 불가능해서다.
이게 무슨말이냐면 상속을 받는것에선 부모가 자식한테 물려주는건 가능하다.왜냐? 부모는 자기가 가지고 있는걸 그대로 물려주면되니깐 근데 자식이 부모로 변한다는것은 문제가 있다.
자식은 자식것을 나름대로 가지고있고,거기에 추가로 부모것을 가지고 있는 상태다.근데 자식 포인터 변수가 부모 객체를 가리킨다거나 부모 객체로 변하고싶으면 부모객체와 동일시되기위해 자식객체가 가지고 있던것(맴버변수 + 맴버함수)을 내려놔야한다.
코드란건 한번 정해놓으면 빌드까지 변화없이 가야되는데 중간에 코드를 버린다는것은 뭔가 잘못된것이다.곧 문법적으로 가능하지않은 상황이다.
근데 STATIC_CAST는 부모를 자식으로 바꿨었는데 그건 되냐? -> 문법적으로 잘못된것임에도 불구하고 강제로 바꾼것
(필드와 매서드가 진짜 사라진게 아니라 사라진 척 하는 것)
DYNAMIC_CAST는 부모 객체를 자식으로 바꾼다.부모객체가 자식객체로 변할려고하면 자식객체에 있는 그 멤버변수, 멤버함수가 추가되어야하는데 그것도 불가능하다.코드가 없기때문이다.만약에 상속 관계였으면 물려받은 코드가 있을탠데 이 역순의 경우는 부모가 자식으로 변한기위해 자식과 같은 코드를 가지고있지않기에 말이 안된다.
그래서 DYNAMIC_CAST의 경우 부모 객체의 주소값이 있지만 그것을 자식 객체 포인터 변수로 캐스팅하는 과정에서 에러가 나는 상황이면 NULL을 리턴한다.그러면 자식클래스 포인터 SS는 NULL을 가리킨다.
STATIC_CAST의 경우는 NULL이 안나오는데 이게 문제가 되는게 뭐냐면 포인터변수에는 주소값이 들어있다.근데 이 포인터변수가 바뀐척만 하고 내용물은 그대로여서 프로그램 테스트 때는 통과가 되지만(NULL이 아니니깐) 나중에 프로그램을 실행했을때 에러가 나게된다.
반면에 DYNAMIC_CAST는 캐스팅 할 수 없는 상황에서는 NulL로 바꿔준다.-> NULL이 띄어짐으로 인해 캐스팅이 제대로 안됐구나하고 인지가 가능
4.STATIC와DYNAMIC의 차이
-> 성능과 안전성이다.
STATIC 같은 경우는 정상적인 포인터인지 확인이 불가
단순변환이여서 속도가 빠름
용도 : 코드상으로 무조건 A가 B로 변하는 경우
DYNAMIC 같은 경우는 내가 NULL 체크만하면 정상적 캐스팅인지 확인 가능
단,단점 두 가지가 있음
ㄱ.상속받은 클래스 밖에 못쓴다.
ㄴ.느리다.
케스팅이 되는지의 판단을 Virtual 테이블(가상함수테이블)을 가지고 판단하는데 바꿀 수 있는 타입이면 바꾸고, 못바꾸면 NULL로 처리한다.이걸 참조하게되면서 속도가 느려지는 이슈가 있다.
*부모의 Virtual 함수가 하나라도 존재시 가상함수테이블이 생김
용도 : A가 뭘로 변할지 확신할 수 없는 경우
ex) 자식객체가 100개 있을때
소중한 정보 잘 봤습니다!