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[]
를 부르는 방법으로 *this
의 const
operator[]
를 부르기 위한 캐스팅이다. 겉에 하는 casting은 반환 시 const
값을
떼어내고 반환하는 캐스팅이다. 호출자 입장에서는 const
상수 멤버함수를 부르는
것이 아닌 비상수멤버함수를 요청했기 때문에, const
를 떼고 값을 반환해야한다.
위와 같이 사용할 경우 우리는 코드 중복 없이 상수/비상수 멤버함수를 호출 할 수
있다.
Appendix
const
는 대단한 축복이다. 포인터나 반복자에도 그렇고, 객체에 대해서도, 메개변수에 대해서도 심지어 멤버함수에 대해서도.. const
를 써야하는 상황이 온다면 무서워 하지말고 쓰도록 하자.
- const를 붙여 사용하면 컴파일러가 사용성 에러를 잡을 수 있도록 도와준다.
- 컴파일러 쪽에서 보면 비트 상수성을 지켜야 하지만, 논리적 상수성을 사용해 프로그래밍 해야한다.
- 상수 멤버 및 비상수 멤버 함수가 기능적으로 서로 똑같이 구현되어 있을 경우 코드 중복을 피하는 방법을 사용하자. 이때, 비상수 버전이 상수 버전을 호출하도록 만들자.