계속해서 저번 포스트에 이어서 이번에는 const에 대해서 다뤄보겠다.
나름대로 포인터와 참조자 쪽에서 중요한 요점들이 약간씩 있기 때문에
그 부분들은 약간씩 강조해 보겠다.
1. const에 대한 참조자
- 참조자를 const 타입 객체에 결합이 가능하다.
즉, const 변수에 대한 참조자라는 뜻.
const int ci = 1024; //올바른 사용.
const int &r1 = ci; //올바른 사용.
r1 = 42; //error : r1이 const이므로 수정불가.
int &r2 = ci; //error : const 변수를 참조시 참조자도 const여야 함.
위에를 보듯, 무조건 const 참조자는 어떤 const의 대한 참조자이다.
즉, const 변수를 참조하기 위해서는 무조건 그 변수를 참조하는 참조자 또한
const 타입이어야 한다.
1-1. 초기화와 const에 대한 참조자.
int i = 42;
const int &r1 = i;
const int &r2 = 42; //const 참조자는 상수(숫자)로 초기화도 가능.
const int &r3 = r1 * 2;
위의 예시는 모두 올바른 사용에 대한 예시이다.
const에 대한 참조자를 초기화 할 수 있으며,
이는 초기화시 참조하는 객체가 const가 아니어도 무관하다.
약간 윗 내용과 헷갈릴 수 있는데,
다시 말하자면 const 변수를 참조할때는 무조건 참조자도 const여야 하지만,
일반 변수를 참조할때는 참조자가 const 참조자이든 아니든 딱히 상관없다.
1-2. 임시객체란?
- 임시 객체는 표현식을 평가한 결과를 저장할 공간이 필요할 때,
컴파일러에서 생성한 이름 없는 객체이다.
아래 예시)
int &r4 = r * 2; //보통의 정의.
double dval = 3.14;
const int &ri = dval; //int참조자가 double 변수를 참조하므로 소실되는 데이터 발생.
위에 코드에 보이다시피, 참조자 ri은 int 타입의 참조자임에도 불구하고
double 형 변수 dval를 참조하였다. 때문에 데이터가 조금이라도 소실되는 부분이 생긴다.
이를 해결하기 위해서는 아래 예시로 예를 들어보자면,
const int temp = dval; //double을 사용해 임시 const int를 만듬.
const int &ri = temp; //ri를 생성한 임시 객체와 결합.
즉, 참조자 ri와 결합한 객체가 int라는 것을 보장하게 만들어버리는 것이다.
2. 포인터와 const에 대해서.
이제 많이 헷갈려 하는 부분에 대해서 진도를 나가보겠다.
나도 책보고 배우면서 헷갈렸다가 결국엔 인터넷 검색해보고서 다시 깨닫게 되었다..
(번역이 진짜 좋지는 않은듯..)
일단 const 변수에서 포인터가 어떻게 몇가지로 분류되어 쓰이는지 살펴보자면,
int a = 100;
int b = 200;
const int *ptr = &a;
//값만 상수, 포인터부분은 일반변수.
//(가리키는 변수를 b로 바꿀수 있지만 a속의 100이라는 값을 다른걸로 못바꿈.)
//포인터가 가리키는 객체는 변경가능하지만, 가리키는 숫자는 변경 불가.
const int const *ptr = &a;
//int도 상수화, 포인터 부분도 상수화. 즉, 초기화시에 이주에는 (모든 값을 못바꿈.)
//상수 값과 가리키는 객체는 초기화 이후에 모두 못바꿈.
int const *ptr3 = &a;
//상수값은 이후에도 변경가능, 포인터 변수가 가리키는 객체는 못바꿈.
//포인터 부분이 상수화됨.
간단하게 설명하자면 이렇게 된다.
이제 포인터 const에 대해서 두가지 특징들을 설명하자면,
- 참조자처럼 포인터로 const 나 const가 아닌 객체를 가리키도록 정의가능하다.
- const에 대한 포인터도 가리키는 객체를 바꿀 수 없다.
아래 코드로 예시를 들어보겠다.
const double pi = 3.14; //pi는 const이므로 값을 못바꿈.
double *ptr = π //error! : ptr은 보통의 포인터.
const double *cptr = &p; //가능함.(const 포인터라서)
*cptr = 42; //error! : *cptr에 대입을 못함.(숫자는 못바꿈)
즉, 포인터의 타입이 객체와 동일해야 한다.
(일반이면 일반 포인터로, const 객체면 const 포인터로.)
다만 조금 예외가 있다면...
double dval = 3.14; //dval은 double이고 일반 객체이다.
cptr = &dval; //좋지만.... cptr이 cosnt라서 값을 못바꿈.
- 참조자처럼 포인터가 const이지만, 포인터가 가리키는 객체가 const인지는 알 수 없음.
3. 상위 const와 하위 const
- 상위 const : 포인터 그 자체가 const일때.
- 하위 const : const 포인터나 참조자가 가리키는 대상객체가 const 타입일 경우.
3-1. 상위 const에 대해서.
상위 const는 객체 자체가 const임을 뜻함(일반적으로)
때문에 상위 const는 내장산술타입, 클래스, 포인터등 다 가능.
- 다른 타입들과 달리 상위 하위 포인터는
const를 서로 독립적으로 쓰기에 유의해야 함.
int i = 0;
int *const p1 = &i; //p1값은 못바꿈. const 상위.
const int ci = 42; //ci는 못바꿈. const 상위.
const int *p2 = &ci; //p2는 바꿀수 있음. const 하위.
const int &const p3 = p2;
//가장 오른쪽 const는 상위, 가장 왼쪽은 상위가 아님.
const int &r = ci; //참조자 타입 const는 항상 하위.
상위 하위 차이는 객체를 복사할때 매우 중요하다.
객체 복사시 상위 const는 무시하기 떄문이다.
i = ci; //ci 값으로 복사하므로 ci에서 상위 const는 무시.
p2 = p3; //가리키는 타입이 일치. p3에서 상위 const는 무시.
- 객체 복사시 복사하는 객체를 바꾸지 않기 때문에.
복사하는 객체나 결과를 담는 객체가 const인지 여부는 중요하지 않음.
3-2. 하위 const에 대해서.
- 객체를 복사할 때 두 객체 모두 하위 const에서 한정표시가 있거나,
두 객체 타입을 서로 변환이 가능해야 함. 일반적으로, const가 아닌 객체는 const객체로 변환이 가능하지만,
그 반대는 불가능 하다.int *p = p3; //error! : p3에는 하위 const가 있지만, p는 없음. p2 = p3; //p2, p3에 같은 하위 const가 한정표시 존재. p2 = &i; //int *를 const int *로 변환 가능. int &r = ci; //error! : 보통의 int& const int와 결합 불가. const int &r2 = i; //const int&를 보통의 int와 결합 가능.
- 보통의 int타입이므로 p3를 초기화 하지 못함.
'Game DevTip > C++' 카테고리의 다른 글
6. C++ string 타입에 대해서 (0) | 2024.12.05 |
---|---|
5. C++ auto & decltype에 대해서. (0) | 2024.12.05 |
4. C++ Typedef(타입 별칭)에 대해서 (0) | 2024.12.05 |
3. C++ constexpr에 대해서 (1) | 2024.12.05 |
1. C++ 변수사용 Tip에 대해서. (0) | 2024.12.05 |
댓글