일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- moreeffectiveC++
- UE_LOG
- 언리얼엔진구조체
- dataasset
- C++
- 데이터애셋
- 선택정렬
- 정렬
- 스마트포인터
- 약참조
- 알고리즘
- 강참조
- 자료구조
- map
- BFS
- 프로그래머스
- C++최적화
- enumasByue
- unorder_map
- 람다사용정렬
- 델리게이트
- UE4 커스텀로그
- stl
- 애셋로드
- 람다
- UML관련
- 언리얼가비지컬렉터
- 정렬알고리즘
- 크리티컬섹션
- UELOG
- Today
- Total
기억을 위한 기록들
[CPP-effective] 4-5 데이터 멤버가 선언될 곳은 private 영역이라고 생각하자. 본문
[CPP-effective] 4-5 데이터 멤버가 선언될 곳은 private 영역이라고 생각하자.
에드윈H 2021. 4. 15. 18:05
그렇게 생각해야하는 이유 2가지
첫 번째 이유
문법적 일관성이다. 데이터 멤버가 public이 아니라면, 사용자 쪽에서 어떤 객체를 접근할 수 있는 유일한 방법은 멤버 함수일 것이다. 그러면 괄호를 붙어야 하는지 말아야 하는지 고민할 필요가 없어진다.(무조건 괄호)
Vector a;
a.DoSomething(); //괄호를 붙어야할지 말아야할지 고민할 필요가 없을 것이다.
그리고 멤버변수가 public이면 읽기뿐만 아니라 쓰기 권한을 통제하기 애매해진다. getter/setter 등으로 확실히 하는 게 좋다.
두 번째 이유
캡슐화이다. 함수를 통해서만 데이터 멤버에 접근할 수 있도록 구현해두면, 데이터 멤버를 계산식으로 대체가 가능할 수도 있다.
상황 예)
예를 들면 여러 캐릭터를 조종 할 수 있는 게임을 만든다고 할 때, N명의 캐릭터들의 평균 체력을 모니터링하는 클래스가 있다고 칩시다.
class HealthData {
//..
public:
double GetAverageHealth() const;
//..
};
이제 저 GetAverageHealth() 멤버 함수를 어떻게 구현할지 생각해보면, 2가지 방법이 보통 있는데,
1. 캐릭터들의 체력들의 평균값을 계속해서 담는 어떤 데이터 멤버를 클래스 안에 넣기
2. 데이터멤버 없이 함수 호출될 때마다 평균값을 가져와 반환하기
이렇게 두가지가 떠오른다.
1번 방법(현재 평균값 유지하기)을 사용하게 되면 평균값을 유지하기 위한 데이터 멤버가 추가됨으로, HealthData클래스가 커지게 된다. 현재의 평균값, 데이터의 개수 등이 데이터 멤버로 들어가야 하기 때문이다. 그 대신에 함수 자체는 반환만 하면 되기에 효율면에서 나쁘지 않다.
2번 방법은 HealthData클래스 객체의 크기는 작아지지만, 함수 반환 속도는 조금 느려질 수도 있다.
두가지 방법 중에 정답은 없다. 메모리의 공간을 생각하면 2번 방법(매번 계산)이 더 좋을 수도 있고, 속도가 중요하고 메모리가 넉넉하면 1번 방법이 더 나을 수도 있다.
메모리 사용 | 반환속도 | |
1번 방법 (평균 값 데이터 멤버 추가) |
추가 사용 | 빠르다 |
2번 방법(평균 값 계산) | 기본 | 느려진다. |
(적은 데이터라면 체감은 되지 않는 수준이다.)
어쨌든 중요한건 "평균값 접근에 멤버 함수를 통하게 한다"라는 것이다.(평균값을 캡슐화한다)
캡슐화는 클래스의 불변속성을 항상 유지하는데 절대로 소홀해질 수 없게 된다. 그러니 public으로 데이터 멤버를 드러내는 것을 지양하자.
protected 도 마찬가지?
protected 데이터 멤버의 경우도 비슷하다.
예를 들어 어떤 public이든 protected이든(private이 아닌 곳) 안에 데이터 멤버가 있다고 치자.
만약에 중간에 어떤 문제로 인해 해당 데이터 멤버를 제거하게 되었을때, 같이 지워줘야 하는 코드가 얼마나 많아질까?
짧은 줄이면 몰라도 수십만 줄 되는 코드에서 객체의 호출로 사용된 public 데이터 멤버라든가, 상속받은 클래스 객체의 protected 데이터 멤버를 다 지워줘야 한다... 지우긴커녕 파악하는 것도 힘들 것이다. 코드를 다 다시 써야 하고, 테스트도 다시 하고, 컴파일도 다시 하고... 으으
캡슐화의 관점에서 쓸모 있는 접근 수준은 private(캡슐화 제공)과 private이 아닌 나머지(캡슐화 X), 이렇게 둘뿐이라고만 생각하자.
정리
* 데이터 멤버는 private 멤버로 선언하자. 이를 통해 클래스 제작자는 문법적으로 일관성 있는 데이터 접근 통로를 제공할 수 있고, 필요에 따라서 세밀한 접근 제어도 가능하며, 클래스의 불변속성을 강화, 내부 구현의 융통성도 발휘할 수 있다.
'C & CPP > Effective C++' 카테고리의 다른 글
[CPP-effective] 4-7 타입 변환이 모든 매개변수에 대해... (0) | 2021.04.19 |
---|---|
[CPP-effective] 4-6 멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자. (0) | 2021.04.15 |
[CPP-effective] 4-4 함수에서 객체를 반환해야 할 경우에 참조자를 반환하려 하지말자. (0) | 2021.04.13 |
[CPP-effective] 4-3 '값에 의한 전달'보단 '상수객체 참조자에 의한 전달'방식이 대게 낫다. (0) | 2021.04.13 |
[CPP-effective] 4-2 클래스 설계는 타입 설계와 똑같이 취급하자. (0) | 2021.04.12 |