1장 C++에 왔으면 C++의 법을 따릅시다 🏳

Effective C++ 책을 읽고 정리하고자 합니다. 해당 포스팅은 이전 포스팅에 연계되어 진행됩니다.
이전포스팅을 읽고 해당 포스팅을 읽으면 더욱 이해하기 편하실 겁니다👏

🖇 소스코드에 마우스를 올리고 copy 버튼을 누를 경우 더 쉽게 복사할 수 있습니다!

궁금한 점, 보안점 남겨주시면 성실히 답변하겠습니다. 😁
+ 감상평 댓글로 남겨주시면 힘이됩니다. 🙇

상수멤버 및 비상수 멤버 함수에서 코드 중복

앞서 정리한 mutable은 내가 생각지도 못한 비트수준 상수성은 무슨말이냐.. 라는 문제를 한번에 해결 해준다. 꽤 괜찮은 방법은 확실하지만, 아직 const 관련해 모든 문제가 해결 되지 않았다. 이런 생각을 해보자. TextBlock (or CTextBlock)operator[] 함수가 지금은 특정 문자만 반환하고 있지만, 프로젝트가 커져 해당 함수에서 mutable을 활용해 경계검사, 접근 데이터 로깅 그리고 자료 무결성 검증 등과 같은 다양한 행위들이 추가 됐다고 해보자. 그럴 경우 아래의 코드가 만들어진다.

class TextBlock {
public:
  ...
  const char& operator[] (std::size_t position) const
  {
    cout << "상수 멤버함수 입니다" << endl;

    ...   // 경계검사 코드
    ...   // 접근 데이터 로깅 코드
    ...   // 자료 무결성 검증 코드

    return text[position];
  }

  char& operator[] (std::size_t position)
  {
    cout << "상수 멤버함수 입니다" << endl;

    ...   // 경계검사 코드
    ...   // 접근 데이터 로깅 코드
    ...   // 자료 무결성 검증 코드

    return text[position];
  }

private:
  std::string text;
};

프로젝트가 커져 이래저래 코드를 상수/ 비상수 멤버함수에 많이 넣게 되면 위 코드와 같이 코드 중복이 발생하게 된다. 코드 중복이 생기게 될 경우 컴파일 시간, 유지보수, 코드 크기 부풀림 자질구리한 문제들이 생기게 된다. 생각해보면, return text[postion]; 이라는 부분도 중복 되고 있는 것이다.. 😨

중복을 피할 수 있는 방법이 없을까?

상수 멤버 및 비상수 멤버 함수 코드 중복 해결하는 방법

operator[]의 상수 버전과 비상수 버전을 비교했을 때, const 반환 유무를 제외하고는 동일하게 동작한다. 그렇다면, const를 제거하든, const를 넣어 다른 함수를 불러주게 된다면 코드 중복을 피할 수 있지 않을까? 하는 생각을 할 수 있다. 우리는 코드의 안정성을 위해 비상수 멤버 객체에서 const를 추가해 상수 멤버함수를 부르는 쪽으로 구현하고자 한다.

왜 비상수 멤버 객체에서 const를 추가하는 방법이 안정성이 있다고 할까?

비상수 멤버에는 변할 수 있는 비상수 멤버 객체, 변할 수 없는 상수 멤버 객체를 둘 다 받을 수 있다. 그리고, 변할 수 있는 비상수 멤버 객체에 const를 추가하는 것이 const의 원리를 침해하지 않는다. 생각해보자. 만약 상수멤버 객체를 받은 후 const를 제거하게 된다면, 해당 상수 멤버 객체의 본질 “값의 변화가 존재하면 안됨”을 벗어나게 된다. 따라서 우리는 상수 멤버 객체에 const를 추가하고자한다.

class TextBlock {
public:
  ...

  const Char& operator[] (std::size_t position) const // 이전과 동일한 비상수 멤버함수
  {

    ...   // 경계검사 코드
    ...   // 접근 데이터 로깅 코드
    ...   // 자료 무결성 검증 코드

    return text[position];
  }

  char& operator[] (std::size_t position)
  {
    cout << "상수 멤버함수 입니다" << endl;
    return
      const_cast<char&>(
          static_cast<const TextBlock&>
            (*this)[position]
          );
  }

private:
  std::string text;
};

위의 코드는 코드 중복을 제거한 코드이다. 코드를 보면 캐스팅이 두번 일어나고 있는데, 첫 번째 const는 상수 operator[]를 부르는 방법으로 *thisconst operator[]를 부르기 위한 캐스팅이다. 겉에 하는 casting은 반환 시 const값을 떼어내고 반환하는 캐스팅이다. 호출자 입장에서는 const상수 멤버함수를 부르는 것이 아닌 비상수멤버함수를 요청했기 때문에, const를 떼고 값을 반환해야한다. 위와 같이 사용할 경우 우리는 코드 중복 없이 상수/비상수 멤버함수를 호출 할 수 있다.

Appendix

const는 대단한 축복이다. 포인터나 반복자에도 그렇고, 객체에 대해서도, 메개변수에 대해서도 심지어 멤버함수에 대해서도.. const를 써야하는 상황이 온다면 무서워 하지말고 쓰도록 하자.