티스토리 뷰
해당 글은 '윤성우의 열혈 C++ 프로그래밍'을 참고하여 작성함.
나름 C언어 공부를 많이 했다고 생각하고 있었는데 처음 보는 개념이 나왔다. 정리도 할 겸 기록으로 남겨둔다.
먼저 C/C++에서의 const
키워드가 의미하는 바는 '초기화 후 재초기화가 불가능한 수' 즉 변하지 않는 수이다. 초기화 후 재초기화 함으로써 계속해서 값을 변경할 수 있는 일반적인 변수와 차이가 있다.
이러한 특성으로 const
키워드가 붙은 변수는 정의 시에 항상 초기화 해야 하고 이후 다시 값을 변경하지 못한다.
즉 다음과 같은 경우 컴파일 오류가 발생한다.
//const int var; //오류 발생! (초기화 하지 않음)
const int var2 = 10;
//var2 = 20; //오류 발생! (재초기화)
책에서 제시한 개념은 다음 4개 코드의 차이점이다. 정말 그런지 하나씩 검증해보고자 한다.
const int num = 10; //변수 num을 상수화
const int* ptr1 = &val1; //포인터 ptr1을 이용해서 val1의 값을 변경할 수 없음
int* const ptr2 = &val2; //포인터 ptr2가 상수화 됨
const int* const ptr3 = &val3; //포인터 ptr3가 상수화 되었으며, ptr3를 이용해서 val3의 값을 변경할 수 없음
const int num = 10;
#include <iostream>
using namespace std;
const int num = 10;
//num = 5; //오류 발생!
const int* ptr1 = &val1;
int val1 = 10;
int val2 = 20;
const int* ptr1 = &val1;
cout << *ptr1 << endl; //10
val1 = 5;
cout << *ptr1 << endl; //5
//*ptr1 = 7; //오류 발생!
ptr1 = &val2;
cout << *ptr1 << endl; //20
ptr1
포인터를 통해 해당 포인터가 가리키고 있는 val1
변수의 값 변경은 불가능하지만, val1
변수 자체는 상수가 아니므로 직접 변경은 가능하다. 또한 ptr1
포인터 변수 또한 상수가 아니므로 기존 val1
변수를 가리키던 것을 val2
변수를 가리키도록 변경할 수 있다. 즉 포인터 변수 값의 변경은 가능하다.
int* const ptr2 = &val2;
int val2 = 20;
int val3 = 30;
int* const ptr2 = &val2;
cout << *ptr2 << endl; //20
*ptr2 = 15;
cout << *ptr2 << endl; //15
//ptr = &val3; //오류 발생!
이 경우 ptr2
포인터 변수를 통해 해당 포인터가 가리키고 있는 val2
변수 값의 변경이 가능하다. 그러나 ptr2
포인터 변수는 const
키워드가 붙은 상수이므로 변수 정의 시 초기화한 val2
변수의 주소값을 val3
변수의 주소값으로 변경하는 것은 불가능하다.
const int* const ptr3 = &val3;
int val3 = 30;
int val4 = 40;
const int* const ptr3 = &val3;
//*ptr3 = 25; //오류 발생!
//ptr3 = &val4; //오류 발생!
val3 = 35;
cout << val3 << endl; //35
이 경우는 ptr3
포인터 변수가 가리키고 있는 변수 값을 ptr3
를 통해 변경하는 것도 불가능하고, ptr3
포인터 변수 자체의 값 변경도 불가능한 것을 볼 수 있다. 당연히 val3
, val4
변수는 const
키워드가 없으므로 직접 값 변경은 가능하다.
추가적으로 C에는 없는 개념인 reference 즉 참조 변수에 const
를 붙이면 어떻게 될까 궁금증이 생겼다.
먼저 참조 변수란 '자신이 참조하는 변수를 대신할 수 있는 또 하나의 이름' 이며 다음과 같이 선언 및 초기화한다.
int num = 10;
int& ref = num;
cout << num << endl; //10
cout << ref << endl; //10
이 경우 변수 num
에 대한 ref
라는 별칭(alias)이 생긴 것이다. 따라서 10이라는 값이 저장되어 있는 메모리 공간에 num
또는 ref
변수를 통해 접근할 수 있다.
참조자의 특징으로는
1. 참조자는 변수에 대해서만 선언이 가능하고 선언됨과 동시에 누군가를 참조해야 한다. (상수 불가)
2. 참조 대상 변경이 불가능하다.
3. 참조자를 선언하면서 NULL로 초기화가 불가능하다.
//int& ref = 10; //오류 발생! (상수 참조)
//int& ref2; //오류 발생! (선언 시, 참조 대상 없음)
int num = 10;
int num2 = 20;
int& ref3 = num;
ref3 = num2; //**
//&ref3 = num2; //오류발생! (참조 대상 변경)
//int& ref4 = NULL; //오류발생! (NULL 참조)
여기서 살펴볼 점은 &ref3 = num2;
는 컴파일 오류가 발생하는 한편 ref3 = num2;
는 오류가 발생하지 않는다는 점이다. 먼저 위 int& ref3 = num;
코드를 통해 num
이라는 변수에 ref3
라는 별칭이 생성되었다. ref3 = num2;
코드는 별칭인 ref3
를 통해 참조하는 변수의 메모리 공간에 접근하여 기존 10의 값을 20으로 변경하는 것이지만, &ref3 = num2;
코드는 ref3
변수의 참조를 기존 num
변수에 대한 것에서 num2
변수로 변경하는 것이므로 오류가 발생하게 되는 것이다.
밑의 int& ref4 = NULL;
코드에서는 'C++ 비const 참조에 대한 초기 값은 lvalue여야 합니다.' 라는 오류 메시지가 뜨는데 오류가 발생하는 이유를 좀더 생각해보자면, ref4
라는 별칭을 통해 접근할 메모리 공간이 없기 때문이라고 볼 수 있다.
여기서 lvalue란 '= 연산자 왼쪽에 올 수 있는 것으로서, 하나의 값을 저장할 수 있는 메모리에 대한 참조'이다. 만약 ref4
참조 변수에 const
키워드가 붙는다면(const int& ref4 = NULL;
) 해당 코드에 대한 오류는 발생하지 않는다. (ref4
별칭을 통해 참조하는 값을 변경할 일이 없기 때문 즉 ref4
변수를 통해 메모리 공간에 접근할 일이 없기 때문, 이 부분이 이해되지 않는다면 아래를 먼저 읽어보시길.)
이제 참조 변수에 const
키워드를 붙여보자.
cout << endl;
int num = 10;
int num2 = 20;
int& const ref = num;
cout << ref << endl; //10
ref = 15;
cout << ref << endl; //15
num = 17;
cout << num << endl; //17
cout << ref << endl; //17
ref = num2;
cout << ref << endl; //20
이 경우 오류가 발생하지 않고 실행이 잘 된다. ref
변수 바로 앞에 const
키워드가 붙었으므로 참조 변수 ref
자체가 상수화 되었다고 해석하면 된다. 다만 ref
변수가 참조하는 num
변수는 상수가 아니므로 ref
변수를 통해 값 변경이 가능하다. 여기서 ref
변수가 상수화되었다는 의미는 ref
변수의 참조 대상 변경이 불가능하다는 것으로 해석할 수 있는데 이는 원래 참조 변수의 특징이다. 따라서 참조 변수 바로 앞의 const
키워드 유무에 따른 차이는 없는 듯하다.
int num = 10;
int num2 = 20;
const int& ref = num;
cout << ref << endl; //10
//ref = 15; //오류 발생!
num = 15;
cout << num << endl; //15
cout << ref << endl; //15
//ref = num2; //오류 발생!
반면 const
키워드를 맨 앞에 붙였을 때에는 ref
별칭을 통해 num
변수 값을 변경하는 것이 불가능하다로 해석할 수 있다. 다만 num
변수 자체는 상수가 아니므로 직접 재초기화가 가능하며 따라서 별칭인 ref
로 해당 변수에 접근하였을 때 값이 변경된 것을 확인할 수있다.
const int num = 10;
const int& ref = num;
const int& ref2 = 20;
cout << ref << endl; //10
cout << ref2 << endl; //20
//ref = 15;
//num = 15;
추가로 위와 같이 참조 변수가 상수를 참조할 경우에는 참조 변수에 const
키워드를 붙여주어야 한다. 즉 참조 변수를 통해 상수를 변경하지 못하도록 막아두어야 한다는 것이다.
끝.
'C++' 카테고리의 다른 글
[개념] C++의 형변환 연산자 (0) | 2022.07.02 |
---|---|
[개념] C++의 다형성 - virtual 함수 (0) | 2022.06.27 |
[개념] C++의 상속 3계명? (0) | 2022.06.24 |
[개념] C++의 복사 생성자 (0) | 2022.06.22 |
[개념] C++의 멤버 이니셜라이저 (0) | 2022.06.20 |
- Total
- Today
- Yesterday
- 템플릿 콜백 패턴
- spring boot
- Transaction
- QueryDSL
- rest api
- facade 패턴
- Front Controller
- Assertions
- Gitflow
- github
- 디자인 패턴
- ParameterizedTest
- C++
- SSE
- Git
- Java
- 모두의 리눅스
- 전략 패턴
- JPA
- junit5
- 서블릿 컨테이너
- spring
- servlet filter
- 단위 테스트
- Linux
- spring aop
- FrontController
- mockito
- vscode
- Spring Security
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |