이펙티브 C++
https://www.yes24.com/Product/Goods/17525589
페이지 160p ~ 169p
요약
항목 22: 데이터 멤버가 선언될 곳은 private 영역임을 명심하자
- 데이터 멤버가 public, protected 영역에 존재하면 안되는 이유를 알게 되면, 자연스럽게 데이터 멤버가 private 영역에 선언되어야 한다는 것도 자연스레 알게 될 것이다.
문법적 일관성
- 데이터 멤버가 public이 아니라면 사용자 쪽에서 어떤 객체를 접근할 수 있는 유일한 수단은 멤버 함수일 것이다.
- 따라서 사용자는 해당 클래스의 멤버에 접근하고 싶을 때 그저 함수만 사용하면 된다.
데이터 멤버에 대한 정교한 제어
- public으로 데이터 멤버를 선언했다면 모든 곳에서 이 멤버에 대해 읽고, 쓰는 작업이 가능하지만, 함수를 통해 접근한다면 해당 멤버에 대해 접근 불가, 읽기 전용, 읽기 쓰기 접근을 구현할 수 있다.
class AccessLevels {
public:
int getReadOnly() const
void setReadWrite(int value)
int getReadWrite() const
void setWriteOnly(int value)
private:
int noAccess;
int readOnly;
int readWrite;
int writeOnly;
}
캡슐화
- 함수를 통해서만 데이터 멤버에 접근할 수 있도록 구현하면 캡슐화가 용이하다.
- 데이터 멤버를 계산식으로 교체하거나, 사용자가 클래스를 넘보는 일을 막을 수 있다.
- 사용자로부터 데이터를 숨기면(캡슐화를 하면) 클래스의 불변속성을 항상 유지하는 데 도움이 된다.
- 캡슐화를 한다는 것은 현재의 구현을 나중에 바꾸기로 결정할 수 있는 권한을 예약하는 것과도 같다.
- C++ 세상에서 public은 캡슐화되지 않았다는 의미이며, 실질적으로는 캡슐화되지 않은 코드는 바꿀 수 없다고 봐야한다.
- 위와 같은 내용들은 public 멤버뿐만 아니라 proteected 멤버의 경우에도 모두 적용된다.
항목 23: 멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자
- 다음과 같이 웹브라우저를 나타내는 클래스가 있고, 웹브라우저가 제공하는 다양한 기능들을 제공하는 함수들이 있다고 가정하자.
class WebBrowser {
public:
// 캐시 초기화
void clearCache();
// 방문 기록 제거
void clearHistory();
// 쿠키 제거
void removeCookies();
}
- 어떤 사용자는 웹브라우저 클래스에서 제공하는 이 함수들을 한 번에 호출하고 싶을 수 있기 때문에 세 함수를 모두 호출해주는 함수가 있을 수도 있다.
class WebBrowser {
// clearCache, clearHistory, removeCookies 일괄 호출
void clearEverything();
}
- 이 시점에서 고민할 수 있는 것은 이 기능을 비멤버 함수로 제공할 수도 있다는 것이다.
void clearBrowser(WebBrowser& wb)
{
wb.clearCache();
wb.clearHistory();
wb.removeCookies();
}
- 어떤 방법이 더 나은 방법인지 하나씩 생각해보자.
캡슐화
- 어떤 것을 캡슐화하면, 우선 외부에서 이것을 볼 수 없게 된다.
- 캡슐화를 하면 할수록 그만큼 밖에서 볼 수 있는 것들이 줄어든다.
- 이는 곧 캡슐화된 대상을 바꿀 때 필요한 유연성이 커진다는 것이다.
- 클래스의 데이터 멤버는 private으로 선언해야 캡슐화가 가능해진다.
- private 멤버는 멤버 함수 및 프렌드 함수만 접근이 가능하다.
- 따라서 같은 기능을 제공하는 멤버 함수와 비멤버 비프렌드 함수 중에서 후자의 경우가 캡슐화 정도가 더 높다고 볼 수 있다.
- 비멤버 비프렌드 함수는 어떤 클래스의 private 멤버 부분을 접근할 수 없기 때문에 데이터 멤버에 접근할 수 있는 방법이 제한된다.
- 멤버 함수는 private 멤버의 직접 접근이 가능하기 때문에 더 많은 경우를 생각해야 한다.
비프렌드 함수
- 프렌드 함수를 사용한다면, 캡슐화의 의미가 무색해진다.
- 프렌드 함수는 private 멤버에 대한 접근권한이 해당 클래스의 멤버 함수가 가진 접근권한과 같다.
함수는 어떤 클래스의 비멤버가 되어야 한다
- 이 말을 그 함수가 다른 클래스의 멤버가 될 수 없다라는 것으로 이해하면 안된다.
- 다른 유틸리티 클래스에서 호출해도 된다는 것이다.
- 중요한 것은 private 멤버에 대한 접근권한 때문에 캡슐화에 영향을 주는 클래스는 안된다는 것이다.
- C++에서는 네임스페이스를 이용하면 더 자연스럽게 이를 구현할 수 있다.
namespace WebBrowserStuff {
class WebBrowser {};
void ClearBrowser(WebBrowser& wb);
}
- 네임스페이스는 클래스와 달리 여러 개의 소스 파일에 나뉘어 흩어질 수 있으며,
ClearBrowser
와 같은 편의성 함수는 멤버도 아니고 프렌드 함수도 아니기에 사용자 수준에서는 아무리 애를 써도 private 멤버에 대한 직접 접근 권한을 얻을 수 없다. - 따라서 응용도가 높은 클래스는 이러한 종류의 편의성 함수가 많이 생길 수 있고, 이런 종류의 함수들을 별도의 헤더 파일로 만들어 편의 기능들을 몰아넣으면 된다.
- 표준 C++ 라이브러리는 이런 방식으로 구현되어 있다.(std)
- 편의 함수 전체를 여러 개의 헤더 파일에(그러나 네임스페이스는 하나) 나누어 놓으면 편의 함수 집합의 확장도 쉬워진다.
- 그냥 특정 네임스페이스에 비멤버 비프렌드 함수를 원하는 만큼 추가하면 된다.
메모
항목 22: 데이터 멤버가 선언될 곳은 private 영역임을 명심하자
- 데이터 멤버는 private 멤버로 선언하자. 이를 통해 클래스 제작자는 문법적으로 일관성 있는 데이터 접근 통로를 제공할 수 있고, 필요에 따라서는 세밀한 접근 제어도 가능하며, 클래스의 불변속성을 강화할 수 있을 뿐 아니라, 내부 구현의 융통성도 발휘할 수 있다.
- protected는 public보다 더 많이 보호 받고 있는 것이 절대로 아니다.(실질적으로 동일하다고 봐야 한다.)
항목 23: 멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자
- 멤버 함수보다는 비멤버 비프렌드 함수를 자주 쓰도록 하자. 캡슐화 정도가 높아지고, 패키징 유연성도 커지며, 기능적인 확장성도 늘어난다.
'책 > 이펙티브 C++' 카테고리의 다른 글
이펙티브 C++(183p ~ 190p) (1) | 2024.01.23 |
---|---|
이펙티브 C++(169p ~ 182p) (2) | 2024.01.22 |
이펙티브 C++(149p ~ 160p) (0) | 2024.01.19 |
이펙티브 C++(142p ~ 149p) (0) | 2024.01.18 |
이펙티브 C++(132p ~ 142p) (0) | 2024.01.17 |