본문 바로가기
C++/강좌

[C++ 강좌] const 위치의 의미와 사용 방법

by EZC 2021. 1. 25.
반응형

 

const는 constant의 약자로 명사형 사전적 의미로 "상수"를 뜻합니다. 따라서 말 그대로 C++에서 const는 그 대상을 변경하지 않는 "상수"를 의미합니다.

 

의미하는 바는 굉장히 쉽지만 const 위치에 따라 의미하는 바는 완전히 달라집니다. C++ 뿐만 아니라 다른 프로그래밍 언어에서도 const는 많이 쓰이기 때문에 확실하게 이해하고 넘어갈 필요가 있습니다.

 

목차 (클릭 시 이동)

 

     

    1. const 변수

    1.1) const 비-멤버 변수

    const int num = 1; // 일반적인 표현
    int const num = 1; // 위와 같은 의미
    
    num = 2;           // Compile Error

    위의 두 선언 방식은 의미가 같은데 보통 const 위치가 맨 앞인 첫 번째 형식을 많이 사용합니다. 위와 같이 선언하면 num변할 수 없는 상수가 되는 것이죠. 또한 함수의 반환형이나 매개변수가 const 변수형일 때도 동일한 의미입니다.

     

    보통 코드에서 매직 넘버를 사용하고자 할 때 const 변수를 많이 사용하기도 합니다. 그런데 C++에서 정수형 매직 넘버를 사용하고자 할 때는 enum class를 활용한 enum hack을 사용하는 것이 좋습니다. (이전 "상수 선언 방법" 강좌를 참고하세요.)

     

    1.2) const 멤버 변수

    const 변수는 반드시 선언 시 초기화를 해야 하며 초기화가 되지 않으면 컴파일 에러가 발생합니다. 그래서 class의 멤버 변수를 const로 선언 시에는 반드시 초기화 리스트(Initialize List)를 사용해야 합니다.

    class Foo
    {
    	const int num; // 메모리 할당이 아님
    
    	Foo(void)
    		: num(1) // const int num = 1;
    	{
    	}
    };
    
    class Bar
    {
    	const int num;
    
    	Bar(void)
    	{
    		num = 1; // Compile Error
    		// const int num;
    		// num = 1;
    	}
    };

    Foonum은 생성자에서 초기화 리스트로 1로 초기화 하고 있는데, 이는 const int num = 1;과 동일한 의미입니다.

     

    그런데 Barnum은 생성자에서 초기화 리스트를 사용하지 않고 함수 내부에서 초기화를 시도하고 있습니다. 이것은 초기화가 아니라 const int num;으로 const 변수를 선언한 후에 num = 1;을 시도하는 것이죠. 따라서 const 변수를 1로 수정한다는 의미이므로 컴파일 에러가 발생합니다.

     

    class 내부에서 const int num;을 한 것은 메모리를 할당하는 것이 아니라 단순히 컴파일러에게 class가 어떤 형태인지 알릴 뿐입니다. 따라서 class 내부에서는 const int num;과 같은 형태가 가능하죠.

    그런데 C++11부터는 class 내부에서 const int num = 1;과 같이 선언 및 초기화가 가능하기도 합니다만 초기화 리스트를 사용하여 멤버 변수를 초기화하는 것이 일반적입니다.

     

    1.3) const 포인터 변수

    포인터와 const를 사용할 때는 두가지 경우가 있습니다.

    첫 번째, const 위치가 맨 앞에 있으면서 포인터 변수가 가리키는 값에 대하여 상수화를 시키는 경우입니다.

    int num = 1;
    const int* ptr = # // *ptr을 상수화
    
    *ptr = 2; // Compile Error
    num = 2;  // Pass

    *ptr = 2;ptr이 const 변수이기 때문에 컴파일 에러가 발생 하지만 num = 2;num이 non-const 변수이기 때문에 정상입니다. 즉, 포인터 변수가 가리키는 num 자체가 상수화가 되는 것이 아닙니다.

     

    두 번째, const 위치가 type과 변수 이름 사이에 있으면서 포인터 변수 자체를 상수화 시키는 경우입니다.

    int num1 = 1;
    int num2 = 2;
    int* const ptr = &num1; // ptr을 상수화
    
    ptr = &num2; // Compile Error

    포인터 변수란 대상의 주소 값을 저장하는 변수입니다. 즉, 위의 ptr은 자기 자신을 상수화 시키는 것이기 때문에 num2의 주소값으로 변경하려고 하면 컴파일 에러가 발생합니다.

     

    그리고 위의 두 경우를 모두 사용하려면 아래와 같은 형태가 되는 것이죠.

    int num = 1;
    const int* const ptr = #

     

    사실 C++에는 참조자(Reference)가 있기 때문에 포인터를 사용할 일이 그리 많지 않습니다. 만약 포인터를 상수화를 시키려면 const int* const ptr = #과 같이 다소 번거롭고 가독성이 떨어지는 코드가 되겠지만, 참조자는 애초에 참조하는 대상을 변경할 수 없는 특징이 있기 때문에 const int& ref = num;의 형태로 사용됩니다. ("참조자" 강좌를 참고하세요.)

     

    2. const 멤버 함수

    int GetString(void) const; // Compile Error
    
    class Foo
    {
    	int num = 1;
    
    	int GetNum(void) const
    	{
    		int a = 1;
    		a++;   // 지역 변수는 가능
    
    		num++; // Compile Error
    		return num;
    	}
    };

    const 멤버 함수는 이름 그대로 class의 멤버 함수만 const로 상수화를 시킬 수 있고 비-멤버 함수는 const 함수로 선언이 불가능합니다.

     

    const의 위치는 함수 선언문 맨 뒤이고 되는데 이 의미는 해당 멤버 함수 내에서는 모든 멤버 변수를 상수화 시킨다는 의미입니다. 따라서 위와 같이 멤버 변수인 num은 변경할 수 없고 지역 변수인 a는 변경할 수 있는 것이죠.

     


    여기까지 const 위치에 따른 코드의 의미를 알아보았습니다. 틀리거나 궁금한 점이 있으시면 댓글 부탁드립니다. 감사합니다! :)

    반응형

    댓글