ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • modifier 종류 : const, static, explicit, mutable
    플밍/C++ (overview) 2012. 1. 3. 23:02
    2006/08/04 12:54



    const

     

    (활용예는 다른글에서 써놓았으니 목록에서 찾아보기.)

     

    1. const 멤버 변수

     

    const는 초기화 당시의 값을 절대로 변경할수 없도록 상수화 시키는 키워드 이다.

     

    즉, 선언 당시에 초기화가 되어야 한다.

     

    그런데..

     

    클래스를 정의할때는 멤버 변수를 초기화 할수 없도록 되어 있다.

     

    class AAA{

           const int a;

    public:

           void func1(){

                  a = 10;

           }

    };

     

    그렇다고 이렇게 해버리면, 컴파일오류가 난다. a를 위한 메모리 공간이 할당되면서 쓰레기값으로

    초기화가 되는데, 쓰레기라고 할지라도 그 자체도 값이기 때문이다.

     

    그럼 어떻게?

     

    class AAA{

           const int a;

    public:

           void func1():a(10){

                   ;

           }

    };

     

    굵게 표시한 부분처럼 써주면 된다. 저것을 멤버 이니셜라이져 라고 한다.

     

    둘 이상을 쓸때는 :a(10), b(20), .... 이런식으로 콤마로 연결해주면 된다.

     

    이니셜라이져는 함수의 body보다도 먼저 실행되고const 멤버 변수를 초기화 할수 있다

     

    특징을 갖고 있다.

     

    2. const 멤버 함수

     

    class AAA{

           int a;

    public:

           void func1() const;

    };

     

    이렇게 멤버 함수에 const를 붙여주면, 해당 함수가 멤버 변수의 값을 변경하는걸 막아준다.

    (즉, 클래스 밖에서 정의된 일반 전역 함수에는 쓸수 없다.)

    그것도 아주 철저히 막아준다!!

     

    무슨 말이냐..?

     

    단순히 직접적으로 멤버 변수를 조작하는것(예: a = 10;)뿐만 아니라, 그럴 가능성이 있는

    간접적인 행위 조차도 막는다.

     

    class AAA{

           int a;

    public:

           int* func1() const{

                  return &a;

           }

           void func2() const{

                  func3();

           }

           void func3(){

                  ;

           }

    };

     

    이 문장은 컴파일 에러를 발생시킨다.

     

    우선 func1()에서 에러가 나는 이유는.. 포인터, 즉 a의 주소값을 리턴하고 있는데, 이를 가지고

    조작을 가할 가능성이 있기때문이다.

     

    func2()에서 에러가 나는 이유는? const화 되지 않은 함수를 호출하고 있기 때문이다.

    func2()자체는 전혀 조작에 관련된 일을 하지 않더라도, func3()에서 무슨 짓을 할지 전혀

    알수 없기 때문이다.

     

    즉 const 멤버 함수는 멤버 변수를 조작하는 것을 막고,

    상수화되지 않은 함수의 호출을 금지하고,

    멤버 변수의 포인터를 리턴하는것을 금지한다.

     

    [해결책]

     

    class AAA{

           int a;

    public:

           const int* func1() const{

                  return &a;

           }

           void func2() const{

                  func3();

           }

           void func3() const{

                  ;

           }

    };

     

    3. const 객체

     

    객체를 만들자 마자 const화 하고, (생성자는 자동으로 실행되고), const화 된 멤버함수만

    실행할수 있다.

    (생성자는 const일 필요 없고, const 붙이면 에러난다 -_-;)

     

    4. const 와 함수 오버로딩

     

    void func() const{}

    void func(){}

     

    둘은 다른 함수로 취급된다. 즉, const를 붙이면 오버로딩이 가능해진다.

     

    class AAA{

    public:

           void func() const{};

           void func(){};

    };

     

    int main(){

           const AAA a1;

           AAA a2;

     

           a1.func();   => void func() const 가 호출됨.

           a2.func();   => void func() 가 호출됨.

    }

     

    이런 문장이 있을때, a1는 const화 되어 있으므로, 당연히 const가 붙은 func()만을 호출할수

    있다. a2는 보통으로 선언 되었는데, 그렇다는 것은 const가 붙은 func()를 호출할수도 있고,

    const가 없는 func()를 호출 할수도 있다는 것이다.

     

    그러나 컴파일 에러 없이 잘 수행이 되는데, 즉, 이런 상황에선 const가 없는 func()가 호출된다.

    const가 없는 함수가, const가 붙은 함수보다 우선순위가 높기 때문이다.

     

    5. 정리

     

    const는 다소 불필요하다는 느낌을 줄수도 있는데, 사실은 프로그램을 안정적으로 작성하기

    위해 아주 중요한 키워드 이다. 프로그램의 안정성 향상을 위해 적재적소에 지혜롭게 쓰자.

     

    ===========================================================

    static

     

    한가지 상황을 가정해보자.

     

    main에서 객체를 만들때마다 몇번째로 만든 객체인지를 출력하고 싶다.

     

    그럼 count를 위한 변수를 만들어야 하는데, 분명 객체와 관련 있는거니까 객체 안에

     

    멤버 변수로 넣고 보니, 제대로된 결과가 나오지 않는다.

     

    객체 안에 멤버 변수로 선언된 count는 객체 마다 각각 하나씩 가지고 있는 변수이기 때문에

     

    출력 결과는 늘 똑같이 1번째로 생성된 객체라고 나온다.

     

    그렇다면, count변수를 한 클래스에서 생성된 객체들이 모두 공유할수 있도록 해야겠다.

     

    (그래야겠죠? ^-^;)

     

    이때 쓰는 키워드가 바로 static이다. 즉, 다시 말해 static을 붙여서 변수를 선언하면,

     

    클래스 변수가 되어 버리는 것이다.

     

    [멤버 변수(객체, 인스턴스 변수)와 클래스 변수의 차이에 대해서는 알겠죠?]

     

    class AAA{

           static cnt;

    public:

           AAA(){

                  cnt++;

                  cout << cnt << "th object" << endl;

           }

    };

     

    int AAA::cnt = 0;

     

    int main(){

           cout << AAA::cnt << endl;  (count가 private으로 선언되어 있으므로 에러)

           AAA a1;

           AAA a2;

           return 0;

    }

     

    여기까진 좋았는데.. cnt는 어디서 초기화 시켜야 하나?

     

    static으로 선언된 변수는 전역변수와 마찬가지로 main이 호출되기도 전에 메모리 상에

    올라가게 된다. (그래서, public으로 선언 되어 있다면, 객체를 생성하기 전에도 참조가 가능하다.)

     

    그래서?

     

    static 변수의 초기화는 위의 주황색 굵은 글씨에서 처럼 해준다.

     

    그리고 static으로 선언된 변수는 객체 내에 존재 하지 않는다.

    다만, 객체가 직접적으로 접근할수 있는 권한을 가지고 있을 뿐이다.



     
    그림에서 처럼, cnt는 data영역에 할당되고, a1, a2객체들은 그것을 공유하게 된다.

     

    ===========================================================

    explicit

     

    아예 명시적인 expression만 허용하겠다는 키워드 이다.

     

    예를 들면,

     

    class AAA{

            int a;

    public:

            explicit AAA(int _a){

                    a= _a;

            }

    };

     

    int main(){

            AAA a = 20;    // 정석대로 AAA a(20) 이라고 써야 통과됨.

    }

     

    에러가 나는 이유는, class내의 생성자 생성부분에서 AAA x (xx) 이런 명시적인 표현만

    허용하겠다고 explicit이란 키워드를 붙여놓았기 때문이다.

     

    ===========================================================

    mutable

     

    const를 붙여서 상수화된 멤버 함수는 원칙적으로 멤버 변수를 조작하지 못하게 된다.

     

    그러나, 특정 멤버 변수는 조작할수있도록 하겠다.. 싶으면 그 특정 멤버 변수에 mutable을

     

    붙여서 선언하면 된다.

     

    class AAA{

            mutable int a;

            int b;

    public:

            void func1() const{

                    a = 20;    (a가 mutable이므로 상수화된 멤버함수더라도 조작가능)

                    b = 20;    (b는 보통으로 선언됐고, 상수화된 멤버함수이므로 조작불가능.)

            }

    };

     


    썩 유용한 키워드는 아니다. -_-;

    '플밍 > C++ (overview)' 카테고리의 다른 글

    상속(심화), 두종류binding, virtual  (0) 2012.01.03
    상속  (0) 2012.01.03
    복사 생성자  (0) 2012.01.03
    객체 배열, 객체포인터배열, this, friend  (0) 2012.01.03
    생성자(constructor), 소멸자(destructor)  (0) 2012.01.03
Designed by Tistory.