C++ const

C++에서 const 키워드 관한 고찰

Posted by Start Bootstrap on October 02, 2020

const란

const는 간단히 말해서 개채(Object)나 변수(Variable)를 수정할 수 없도록 컴파일 타임에 보장하는 키워드를 말합니다. 따라서 const 키워드가 붙어 있다면 수정되지 않는 값, 즉 상수라는 것을 공고히 하는 것입니다.

        
                int main() 
                {
                   const int value = 10;
                   value = 20;   // Compile error...
                   value++;   // Compile error..
                }
        
    

해당 코드는 컴파일 에러를 발생하게 합니다. 간단히 말하면 그 이유는 const 변수의 값을 변경하려고 했기 때문입니다. 이 에러를 컴파일 타임에 발생하게 하는 것은 매우 중요합니다. 컴퓨터 뿐만 아니라 동료 프로그래머에게 해당 변수는 절대로 수정되어서는 안된다는 의미를 명백하게 전달할 수 있기 때문입니다.

하지만 주소 값에 const를 붙이는 경우에는 그 값을 수정을 하게 못할 것인지 주소값을 수정하게 못할 것인지를 결정할 수 있다. 이것은 const가 붙은 위치에 따라 달라지는데 사용 방법은 아래와 같다.

        
                int main()
                {
                    const int* pointerVariable	= nullptr;
                    int normalVariable	= 10;
                    const int normalVariable2	= 10;

                    pointerVariable = &normalVariable; // 해당 값을 수정한 것은 아닌 것이 된다. 주소의 값에 const가 걸려 있으므로
                    *pointerVariable += 1; // 주소의 값을 변경하려 했으므로 compile error

                    int* const pointerVariable2 = &normalVariable;

                    pointerVariable2 = &normalVariable2; // 주소에 const변수이므로 변경이 안된다
                    *pointerVariable2 += 1; // 값은 변경 가능

                    const int* const pointerVariable3 = &normalVariable2; // 값과 주소에 모두 const
                    pointerVariable3 = &normalVariable2; // 주소에 const변수이므로 변경이 안된다
                    *pointerVariable3 += 1; // 주소의 값을 변경하려 했으므로 compile error

                    return 0;
                }
        
    

이 규칙은 함수의 파라미터에서도 동일하게 적용된다. 레퍼런스나 주소값에도 동일하게 붙일 수 있다. 가장 중요한 점은 함수를 호출하거나 사용하는 다른 동료들에게 이 인자가 함수 내에서 수정되지 않는다는 보장을 할 수 있다는 점이다.

        
                const std::string getStringValue( const char* pointerVariable, const std::string& referenceVariable);
                // 이 함수가 호출되는 동안에 pointerVariable, referenceVariable값의 변경은 없다라는 것을 보장한다

                int main()
                {
                    const char* pointerVariable = "Variable";
                    const std::string referenceVariable("Variable");

                    const std::String result = getStringValue(pointerVariable, referenceVariable);

                    return 0;
                }
        
    

Class에서의 const

Class에서의 const는 사용하는 경우에 따라 다양한 의미의 const를 가질 수 있습니다. 대표적으로 맴버 함수의 선언과 정의에서 함수 뒤에 붙이는 키워드로 사용할 수 있습니다. 이 경우 이 함수 내부에서 맴버 변수의 변경은 없다라고 보장하는 것입니다.

        
                class Date
                {
                public:
                    Date( int date );
                    int getDate() const;     // A read-only function
                    void setDate( int date ) const;   // A read-only function의 의도
                private:
                    int m_date;
                };

                int Date::getDate() const
                {
                    return m_date;       // const의 의도에 부합하는 함수
                }
                void Date::setDate( int date ) const
                {
                    m_date = date;          // compile error const 키워드가 붙은 맴버함수는 맴버변수를 변경할 수 없다.
                }
        
    

다음과 같이 const 키워드가 붙은 맴버함수는 수정할 수 없다. 하지만 아에 불가능한 것은 아니고 mutable 키워드가 붙은 변수는 가능하다. mutable 키워드는 맴버 함수의 const와 상관없이 값 변경이 가능하다. 그래서 mutable 키워드는 매우 신중하게 사용해야 한다. 꼭 필요한 경우가 많지 않을 것 같기 때문이다.

        
                class Date
                {
                public:
                    Date( int date );
                    int getDate() const;     // A read-only function
                private:
                    int m_date;
                    mutable int m_callCount;
                };

                int Date::getDate() const
                {
                    m_callCount += 1; // const가 붙었는데도 값을 수정할 수 있다

                    return m_date;
                }
        
    

다음과 같이 const 키워드가 붙은 맴버함수는 수정할 수 없다. 하지만 아에 불가능한 것은 아니고 mutable 키워드가 붙은 변수는 가능하다. mutable 키워드는 맴버 함수의 const와 상관없이 값 변경이 가능하다. 그래서 mutable 키워드는 매우 신중하게 사용해야 한다. 꼭 필요한 경우가 많지 않을 것 같기 때문이다.

다음에는 맴버 변수에 const가 붙은 경우이다. 이 경우에는 초기 Class가 생성될 때 값을 초기화 하고 나서는 변경할 수 없다. 즉 Read-Only 변수가 되는 것이다.

        
                class Date
                {
                public:
                    Date(int date);
                    int getDate() const;
                    void setDate(int date);
                private:
                    const int m_date;
                };

                Date::Date(int date)
                    : m_date{date} // 문제없다
                {

                }

                int Date::getDate() const
                {
                    return m_date;
                }

                void Date::setDate( int date )
                {
                    m_date = date; // compile error const 맴버변수는 생성할때만 수정 가능
                }
        
    

constexpr이란

C++11과 C++14에서 constexpr 키워드가 추가되고 발전했습니다. constexpr의 값은 가능 하면 컴파일 시간에 계산 함을 나타냅니다. 따라서 본질은 상수가 필요한 곳(Ex. 배열 선언, template 인수)에 사용할 수 있습니다.

        
                constexpr float variable1 = 30.5; // have no problem
                constexpr float variable2(108); // have no problem
                constexpr float variable3 = exp(5, 3); // call function can constexpr
                constexpr int ii; // 초기화 되지 않는 값은 컴파일 타임에 값을 알 수 없으므로 constexpr 불가능
                int jj = 0;
                constexpr int kk = jj + 1; //constexpr이나 const 변수가 아닌 것은 컴파일 타임에 값을 알수가 없다
        
    

constexpr은 함수에서도 반환 인자에도 사용할 수 있습니다. 마찬가지로 const값을 컴파일 타임에 알수 있는 경우에만 가능합니다.

곧 출시될 C++20에서는 constexpr을 vector나 string같이 stl에서도 적용할 수 있도록 변경점이 예정되어 있습니다. constexpr의 생성자와 소멸자가 추가됨에 따라 가능한 변경점이라고 알고 있습니다. 따라서 constexpr의 활용 범위는 점점 넓어질 것으로 예상되고 매우 중요한 키워드 중 하나가 될 것이라고 생각합니다.

많은 C++ 사용자에게 사랑받고 있는 책 Effective C++의 3장에서는 Use const whenever possible라는 제목으로 const의 중요성과 사용법에 대해서 서술했습니다. 저는 이 부분을 반드시 참고해야 된다고 믿고 있으며 const 키워드를 적극 사용하는 것이 매우 좋은 코드를 생산하는 방법 중 하나라고 생각하고 있습니다.