이번 장에서는 c++을 사용하시면서 위치에 따라 그 의미가 많이 헷갈리는 const 키워드의 사용법에 대해서 정리하고자 합니다.
const의 예외 규칙
const의 적용 규칙은 매우 간단합니다. 항상 const 키워드가 있는 위치에서 왼쪽 대상에 적용됩니다. 규칙이 이렇게 간단한데도 불구하고 사람들이 헷갈리는 이유가 바로 예외 규칙 때문입니다. 다음 예제를 보시면 흔히 쓰는 변수 값에 const를 선언하는 방식입니다.
const int value = 10;
규칙대로라면 다음과 같이 int 다음 const가 나와야 합니다. 그래야 왼쪽에 위치하는 int에 적용할 수 있기 때문입니다.
int const value = 10;
이것이 가능한 이유는 첫 번째 const는 구문 맨 처음에 놓여도 된다는 예외 규칙이 적용이 되었기 때문입니다. 게다가 이 예외규칙을 사람들이 더 많이 쓰기 때문에 더욱더 헷갈리는 것입니다.
위 두가지 예처럼 const를 선언하면 value안에 들어있는 10이라는 값을 고정시킵니다. 다음 예처럼 이 값을 변경하려는 행위가 일어날 때 컴파일 에러를 발생시킵니다.
const int value = 10;
value = 20;
const 포인터
위 예외 규칙을 인지하고 있으면 const 포인터도 쉽게 이해할 수 있습니다. 다음 예을 보면서 그 의미를 설명하겠습니다.
1. const int* value = nullptr;
2. int const* value = nullptr;
3. int* const value = new int;
1번과 2번은 예외 규칙 적용으로 같은 의미입니다. int에 const가 적용된 것으로 포인터가 가리키는 값을 고정시킵니다. 단, 값만 고정시키기 때문에 다음과 같이 포인터는 변경될 수 있습니다.
const int* value = nullptr;
int temp = 8;
value = &temp; //포인터 변경이 가능하다
3번은 포인터에 const를 적용한 것으로 포인터 값을 변경할 수 없습니다. 1번과 2번 예와는 반대로 포인터가 가리키는 값을 변경할 수 있습니다.
int* const value = new int;
int temp = 8;
*value = temp; // 값 변경이 가능하다
포인터도 값도 변경하고 싶지 않으면 다음과 같이 쓰면 됩니다.
int const * const value = new int;
const int* const value = new int;
또한 거의 쓸일이 없겠지만 다음과 같이 포인터가 여러 개 있을 경우에도 동일하게 사용할 수 있습니다.
const int* const* const* const value = nullptr;
const 레퍼런스
const 레퍼런스는 const 포인터보다 간단합니다. 레퍼런스 자체에 const 속성이 있어서 가리키는 대상을 변경하지 않습니다. 그래서 명시적으로 const를 지정할 필요가 없습니다. 또한 레퍼런스에 대한 레퍼런스를 만들 수 없기 때문에 포인터처럼 여러 단계를 사용할 수 없고 오직 한 단계뿐입니다. 보통 다음과 같이 사용합니다.
int a = 8;
const int& ref = a;
ref = 5; //값을 변경할 수 없기 때문에 컴파일 에러
물론 규칙에 의해 const int& ref는 int const& ref와 같이 써도 동일합니다. 여기서 주의할 점은 ref로 접근해서 값을 변경할 수 없지만 a를 통해서는 변경이 가능합니다.
const 레퍼런스는 주로 사용되는 곳이 함수에 매개 변수로 전달할 때입니다. 인수를 전달해서 읽기만 가능하고 변경을 할 수 없게 만들고 싶다면 다음과 같이 사용하면 됩니다.
void func(const int& a)
{
cout << a; // a값을 변경할 수 없고 읽어서 출력만 가능
}
함수에서 const 사용
함수에서 const를 사용할 때는 두가지의 경우가 있습니다. 첫 번째는 클래스 안에서 멤버 함수 뒤에 붙여서 멤버 변수의 변경을 방지하고자 할 때입니다.
class A
{
private:
int a;
public:
int getValue() const
{
a = 10; // 에러
return a;
}
};
만약 멤버 변수 a에 값을 대입한다면 에러가 발생합니다. 보통 멤버 변수의 값을 읽어오는 get() 함수 구현에 많이 사용합니다.
두 번째는 함수의 앞에 붙어서 리턴값을 상수화 시키고 싶을 때 사용합니다. 레퍼런스가 아닌 값을 리턴 시킬 때는 값이 복사되어 리턴되므로 const의 의미가 없지만 레퍼런스를 리턴 시킬 경우 const를 붙이면 리턴값의 변경을 막을 수 있어서 유용합니다.
class CTest
{
private:
int a = 10;
public:
const int& getValue() const
{
return a;
}
};
int main()
{
CTest A;
//int& = A.getValue(); 에러 발생
const int& r = A.getValue();
cout << r;
return 0;
}
[참고]
1. 전문가를 위한 c++(개정4판) (저자 : 마크 그레고리 / 옮긴이 남기혁)
'Program Language > c++' 카테고리의 다른 글
c++ 기초(타입 추론 : auto & decltype) (5) | 2023.01.19 |
---|---|
c++ 기초(레퍼런스) (1) | 2023.01.17 |
c++ 기초(스마트 포인터) (1) | 2023.01.13 |
c++ 기초(동적 배열 할당) (0) | 2023.01.13 |
c++ 기초(포인터 사용법) (0) | 2023.01.12 |
댓글