const는 소스코드 수준에서 의미적인 제약을 주어 나의 실수나 컴파일러가 제약을 위반하는 것을 막는데 한 몫 한다
const는 전역 혹은 네임스페이스 유효범위의 상수 선언(정의), 파일, 함수, static으로 선언한 객체에도 붙일 수 있으며 클래스 내부의 경우에는 정적 멤버, 비정적 멤버 모두 가능하다. 그리고 포인터를 상수로도 바꿀 수 있고 포인터가 가리키는 데이터를 상수로 할 수 있다.
char *p = greeting //비 상수 포인터
//비 상수 데이터
const char* p = greeting //상 수 데이터
char* const p = greeting //상수 포인터
const char* const p = sreeting //상수 포인터, 상수 데이터
이번에는 반복자(iterator)를 예시로 봐보자
const std::vector<int>::iterator iter = vec.begin()
{ //주소를 변경못함(int const)
*iter = 10; //이건 OK
iter++; //이건 에러가 난다.
}
std::vector<int>::const_iterator Citer = vec.begin()
{ //데이터를 변경 못함(const int)
*iter = 10; //데이터를 바꾸려고해서 에러가 난다.
iter++; //이건 OK
}
그리고 const 키워드가 있고 없고 차이가 있는 멤버 함수들은 오버 로딩이 가능하다.
class TextBlock
{
private:
char text[10] = { 0, };
public:
const char& operator[](std::size_t position) const
{
return text[position];
}
char& operator[](std::size_t position)
{
return text[position];
}
}
위에 선언된 TextBlock의 operator []는 다음과 같이 사용할 수 있다.
TextBlock tb("Hello");
std::cout << tb[0] << endl;
const TextBlock CTb("Hello"); //이것은 예시로 들기 위해 쓴것이고 대체로는
std::cout << CTb[0] << endl; //아래의 코드처럼 쓰인다
void print(const TextBlock & CTb) //이 함수에서 CTb는 상수객체로 쓰인다
{
std::cout << CTb[0]; //TextBlock::operator[]의 상수
}
어떤 멤버 함수가 상수 멤버(const)라는 것은 굵직한 양대 개념이 자리 잡고 있다, 비트 수준 상수성(물리적 상수성), 논리적 상수성이다.
비트 수준 상수성은 어떤 멤버 함수가 그 어떤 데이터 멤버를 건드려서는 안 된다는 것이다. C++ 이 비트 수준 상수성이다
하지만 슬프게도 잘못 동작하지만 비트 수준 상수성 검사를 회피하는 경우가 있다 다음 예시를 보자
class CTextBlock
{
public:
.....
char& operator[](std::size_t position) const
{return pText[position];}
private:
char *pText;
}
const CTextBlock CCTb("Hello");
char *pc = &CCTb[0];
*pc = 'J';
이렇게 CCTb는 Jello가 되어버렸다.
논리적 상수성은 이런 황당한 상황을 보안하는 개념으로 나왔다
상수 멤버라고 하여도 사용자 측이 알아채지 못하게 일부 몇 비트 정도는 수정할 수 있게 하여도 상수 멤버 자격이 있다는 것이다. 다음 예시를 보자
class CTextBlock
{
private:
char *pText;
std::size_t text_length;
bool lengthIsValid;
public:
....
std::size_t length() const
}
std:;size_t CTextBlock::length() const
{
if(!lengthIsValId)
{
text_length = std::strlen(pText);
lengthIsValid = ture;
}
}
하지만 이래도 조금 문제가 있다 바로 중복된 코드이다. 같은 op [] 연산자 함수가 상수 버전이랑 비상수버전이랑 똑같이 돌아갈 때 코드가 중복될것이다. 그렇게 되면 매우 귀찮아진다. 그래도 그 중복을 피할 수 있느 방법이 있다. 비상수 버전이 호출되면 상수 버전을 호출해 버리는 것이다
class TextBlock
{
public:
....
const char& operator[](std::size_t position) const
{ ....
....
return text[position];
}
char& operator[] (std::size_t position)
{
return
const_cast<char&> //op[]의 반환타입에 캐스팅을 적용
//const를 떼어낸다
(static_cast<const TextBlock&> //*this의 타입에 const를 붙인다
(*this)[position]; //op[]의 상수 버전을 호출한다.
}
}
상수 버전 op []을 호출하기 위해 static_cast로 형변환을 한 후 반환을 위해 const_cast로 const를 뺐다