낌새만 보이면 const를 들이대 보자

아이템 3 : 낌새만 보이면 const 들이대 보자

  • Const 상수 값을 선언하는 사용된다. 여러 가지 용도로 사용되는데,
  • 상수 포인터
  • STL const_iterator
  • 함수 선언 - 함수 반환 값을 상수로 정해 주면, 안전성이나 효율을 포기하지 않고도 사용자측의 에러 돌발 상황을 줄이는 효과를 자주 있게 된다
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Rational {};

const Rational operator* (const Rational& lhs, const Rational& rhs); //반환 값을 const 로 선언한 이유는

void DoSomething() {
 Rational a, b, c;

 (a * b) = c; //여기서 a*b의 결과에 두고 operator = 을 호출하는 것을 막기 위해
     //const 라서 값 변경 불가능
}


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class TextBlock {
public:
 TextBlock(std::string str) : text(str) {}

 const char& operator[](std::size_t position) const { return text[position]; } //상수 객체에 대한 operator[]
 char & operator[](std::size_t position) { return text[position]; }  //비상수 객체에 대한 operator[]

private:
 std::string text;
};

void DoSomething2() {
 TextBlock tb("Hello"); 
 std::cout << tb[0]; //비상수 멤버 호출

 const TextBlock ctb("World"); 
 std::cout << ctb[0]; //상수 멤버 호출

 tb[0] = 'x'; //문제 없음
 ctb[0] = 'x'; // 컴파일 에러
}


  • 상수 멤버 함수 : 멤버 함수에 붙는 const 키워드는 "해당 멤버 함수가 상수 객체에 대해 호출될 함수이다" 라는 사실을 알려준다. 클래스의 인터페이스를 이해하기 좋게 하기 위해,(변경할 있는 것과 없는 것을 알려준다) 그리고 키워드를 통해 상수 객체를 사용할 있게 하자는 . 가지 중요한 개념이 있는데

  • 비트수준 상수성bitwise constness : 다른 말로 물리적 상수성이라고도 한다. 어떤 멤버 함수가 객체의 어떤 데이터 멤버도 건드리지 않아야(정적 멤버 제외) 멤버 함수가 'const' 임을 인정하는 개념. 객체를 구성하는 비트들 어떤 것도 바꾸면 된다는 .

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class CTextBlock {
public:
 CTextBlock(char * str) : pText(str) {}
 char & operator[] (std::size_t position) const { return pText[position]; } //부적절한 
           //(그러나 비트수준 상수성이 있어서 허용되는)
           //operator[] 의 선언
 
private:
 char *pText;
};

void DoSomething3() {
 const CTextBlock cctb((char*)"Hello"); //상수 객체 선언
 char *pc = &cctb[0];   //상수 버전의 operator[]를 호출하여 cctb의
      //내부 데이터에 대한 포인터를 얻습니다.
 *pc = 'j';  //cctb는 이제 jello 라는 값을 갖습니다. -> 분명 상수 객체임에도 내부 멤버 변수가 변경되었다.
}



  • 논리적 상수성이란 이런 황당한 상황을 보완하는 개념으로 등장하였다. 상수 멤버 함수라고 해서 객체의 비트도 수정할 없는 것이 아니라 일부 비트 정도는 바꿀 있되, 그것을 사용자측에서 알아채지 못하게만 하면 상수 멤버 자격이 있다는

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class CTextBlock {
public:
 CTextBlock(char * str) : pText(str) {}
 char & operator[] (std::size_t position) const { return pText[position]; } 
                    
                    
 std::size_t Length() const;
private:
 char *pText;
 mutable std::size_t textLength;  //이 데이터 멤버들은 어떤 순간에도 수정 가능
 mutable bool lengthIsValid;   //심지어 상수 멤버함수 안이라도 가능
};

std::size_t CTextBlock::Length() const {
 if (!lengthIsValid) {
  textLength = std::strlen(pText); //상수 멤버 함수안에서는 일반적으로 안되지만
  lengthIsValid = true;    //mutable 키워드가 붙어서 가능
 }

 return textLength;
}



  • 상수 멤버 비상수 멤버 함수에서 코드 중복을 피하는 방법.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class TextBlock {
public:
 TextBlock(std::string str) : text(str) {}

 const char& operator[](std::size_t position) const { return text[position]; }
 char & operator[] (std::size_t position) {    //코드 중복 대처 요령(비상수 버전이 상수 버전 호출)
  return const_cast<char&>(     //c++ 에서 캐스팅은 썩 좋지 못한 아이디어지만, 코드 중복도 쉽게 넘길 순 없다.
   static_cast<const TextBlock&>(*this)[position] //상수 버전 operator[]를 호출, *this에 const를 붙이고, 반환 타입에서 const를 뗀다.
   );        //operator[]속에서 operator[] 를 호출하면 재귀적으로 계속 호출하게 되니,
 }          //const operator[]를 호출하기 위한 방법으로 *this 에 const 를 붙인다.(탁월한 방법은 아니지만 차선책)   

private:
 std::string text;
};

댓글

이 블로그의 인기 게시물

Unity - Dialogue 시스템을 구현할 때 유용한 무료 에셋

Unity - 메타 파일

Unity - 라이브러리 폴더