항목 22 데이터 멤버가 선언될 곳은 private 영역임을 명심하자
이번 항목에서는 먼저 데이터 멤버가 왜 public이면 안 되는지 그 이유를 알아보고, 이 이야기가 protected 데이터 멤버에도 똑같이 적용되는 모습을 확인해 보도록 하자. 여기까지 끝나면 데이터 멤버는 반드시 private이어야 한다는 결론을 자연스럽게 볼 수 있을 것이다.
22.1 private가 아닌 데이터 멤버
■ 22.1.1 public 데이터 멤버
public 데이터 멤버를 먼저 도마 위에 올려보자. 왜 데이터 멤버를 public으로 선언하면 안 될까?
① 문법적 일관성
우선 문법적 일관성에 어긋난다. 데이터 멤버가 private이라면, 사용자 쪽에서 어떤 객체에 접근할 수 있는 유일한 수단은 멤버 함수이므로 그 클래스 멤버에 접근하고 싶을 때 괄호 유무를 기억하지 않아도 된다.
② 세밀한 접근 제어
멤버 함수를 사용하면 데이터 멤버의 접근성에 대해 훨씬 정교한 제어를 할 수 있다. 이렇게 세밀한 접근 제어는 어떤 식으로든 외부에 노출되면 안 되는 데이터 멤버가 많기 때문에 나름대로 중요성을 갖고 있다.
class AccessLevels {
public:
int getReadOnly() const { return readOnly; }
int getReadWrite() const { return readWrite; }
void setReadWrite(int value) { readWrite = value; }
void setWriteOnly(int value) { writeOnly = value; }
private:
int noAccess; /* 접근할 수 없습니다. */
int readOnly; /* 멤버 함수를 통해 읽기 전용으로 접근합니다. */
int writeOnly; /* 멤버 함수를 통해 쓰기 전용으로 접근합니다. */
int readWrite; /* 멤버 함수를 통해 읽기 및 쓰기 전용으로 접근합니다. */
};
③ 캡슐화
public으로 선언하면 안 되는 비장의 카드는 바로 캡슐화(encapsulation)다. 함수를 통해서만 데이터 멤버에 접근할 수 있도록 구현하면 사전 조건(precondition), 사후 조건(post-condition), 스레딩 동기화 등 구현상의 융통성을 전부 누릴 수 있다. 또한, 데이터 멤버를 함수 인터페이스 뒤에 감추면 클래스의 불변성(invariance)을 보여줄 수 있는 통로가 멤버 함수밖에 없으므로 불변성을 유지하는 데 도움이 된다.
캡슐화는 현재 구현한 코드를 나중에 수정할 수 있도록 미리 예약하는 것과 같다. public이란 '캡슐화가 되지 않았다'는 뜻이며, '캡슐화되지 않았다'라는 말은 '바꿀 수 없다'는 의미를 갖고 있다. 그러니까 데이터 멤버가 바뀌면 깨질 수 있는 코드의 양에 반비례해서 그 데이터 멤버는 캡슐화 정도가 감소한다.
■ 22.1.2 protected 데이터 멤버
protected 데이터 멤버도 public 데이터 멤버와 똑같다. 문법적 일관성과 세밀한 접근 제어 역시 protected 데이터 멤버에 그대로 적용할 수 있다. 캡슐화도 마찬가지로 protected 데이터 멤버는 public 데이터 멤버보다 절대로 더 많이 가려져 있는 것이 아니다.
22.2 private 데이터 멤버
어떤 클래스에 public 혹은 protected 데이터 멤버가 있고 이것을 제거한다고 가정해보자. 이 멤버를 사용하는 수많은 사용자 코드는 모조리 망가진다. 결국 파악조차 하기 힘든 엄청난 양의 코드를 다시 써야 하고, 문서도 바꿔야 하고, 컴파일도 다시 해야 한다. 캡슐화의 관점에서 봤을 때 의미 있는 접근 수준은 private와 private가 아닌 나머지, 이렇게 둘 뿐이다.
NOTE
① 데이터 멤버는 private으로 선언하자. 이를 통해 클래스 개발자는 문법적으로 일관성 있는 데이터 접근 통로를 제공할 수 있고 필요에 따라 세밀한 접근 제어를 할 수 있다. 또한, 클래스의 불변성을 강화하고 내부 구현의 융통성을 발휘할 수 있다.
② protected는 private보다 더 많이 보호받고 있는 게 절대로 아니다.
'Object Oriented Programming(C++) > Effective C++' 카테고리의 다른 글
Effective C++ | 항목 24 타입 변환이 모든 매개 변수에 적용되어야 한다면 비멤버 함수를 선언하자 (0) | 2022.02.15 |
---|---|
Effective C++ | 항목 23 멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자 (0) | 2022.02.15 |
Effective C++ | 항목 21 함수에서 객체를 반환할 경우에는 참조자를 반환하려고 하지 말자 (0) | 2022.02.03 |
Effective C++ | 항목 20 값에 의한 전달보다는 상수 객체 참조자에 의한 전달이 대개 낫다 (0) | 2022.02.03 |
Effective C++ | 항목 19 클래스 설계는 타입 설계와 똑같이 취급하자 (0) | 2022.02.03 |
댓글