2021 시작

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를 뺐다

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading