2020 이전/C++

이펙티브 C++ 챕터 4 설계 및 선언

이상해C++ 2018. 10. 31. 20:05

캡슐화: 외부에서 볼 수 없다는 것.


캡슐화 할때 밖에서 볼 수 있는 것이 줄어들고, 그것들을 바꿀때 필요한 유연성이 커짐. 캡슐화된 데이터 멤버를 참조하는 인터페이스는 그대로 가져가면서 데이터 멤버한 수정하면 되니까! 또는 코드를 바꿔도 제한된 사용자(해당 데이터/함수)들 외에는 영향을 주지않는 융통성.


C++ 인터페이스 설계

제대로 쓰기에는 쉽게

엉터리로 쓰기에는 어렵게.


18. 인터페이스 설계는 제대로 쓰기에는 쉽게, 엉터리로 쓰기에는 어렵게


함수, 템플릿, 클래스도 인터페이스다. 사용자가 저지를 수 있는 실수를 항상 생각해야한다.


19. 클래스 설계는 type 설계와 똑같이 취급하라.


1) 타입의 객체 생성 및 소멸은 어떻게 이루어질 것인가?

2) 객체의 초기화와 객체의 대입은 어떻게 달라야 하는가?

초기화와 대입은 호출되는 함수가 아예 다르다.

3) 새로운 타입으로 만든 객체가 "값에 의해 전달"되는 경우에는 어떤 의미를 줄 것인가?

CALL BY VALUE. 복사 생성자가 CALL BY VALUE를 실행한다

4) 타입이 가질 수 있는 valid value에 대한 제약은 무엇으로 잡을 것인가?

클래스 멤버 변수 몇개는 반드시 유효해야한다 : 클래스의 불변속성(invariant)

5) 기존 클래스로부터 상속을 받을 것인가?

6) 어떤 종류의 type casting을 허용할 것인가?

만약 T1을 T2로 암시적으로 변환하고 싶으면 T1에 operator T2같은 함수나 인자 한개로 호출되는 비명시 호출을 생성한다. 

7) 어떤 연산자와 함수를 둘 것인가?

8) 멤버 함수 중 어떤것을 외부에 공개할 것인가?

9) 멤버의 접근 권한(public/protected/private)를 어떻게 줄 것인가?

10) '선언되지 않은 인터페이스'로 무엇을 둘 것인가?

타입이 제공할 보장이 어떤 종류일까에 대한 질문. 자원 사용, 수행 성능 및 예외 안정성등으로 클래스 구현에 있어서 제약으로 작용하게 될 사항

11) 새로 만들 타입은 얼마나 generic할 것인가?

12) 정말로 꼭 필요한 type인가?


20. 값에 의한 전달보다는 상수 객체 참조자에 의한 전달이 낫다

pass by value: 전달하는 과정에서 복사 생성자 호출한다.

pass by reference : 상수 객체 참조자(reference to const)

Slicing problem: 파생 클래스가 기본 클래스의 인자에 넘겨질때 파생 클래스의 정보가 손실되고 기본 클래스로 동작하는 문제.  


참조자를 전달한다는 것은 포인터를 전달한다는 것

전달하는 객체의 type이 기본(int char etc)일 경우에는 값으로 넘기는게 효율적일 때가 많다.

반복자와 함수객체는 옛날부터 값으로 전달되도록 설계해왔다.

반복자와 함수객체를 구현할 때는 ①복사효율을 높일 것과 ②복사손실 문제에 노출되지 않도록 만드는 것이 필수.


그렇다면 객체 작고 복사 생성자 비용이 작으면 무조건 값 전달 해도 되나? NO

사용자 정의 double은 레지스터에 안들어가고 실제 double은 들어간다. 하지만 참조자는 포인터로 구현이 되어있기 때문에 확실히! 레지스터에 들어간다.


-> "값에 의한 전달" 이 저비용이라고 가정해도 괜찮은 유일한 타입들

- 기본제공 type

- STL iterator

- 함수 객체


21. 함수에서 객체를 리턴할 경우 참조자를 반환하려 하지마라.

스택 기반: 해당 함수 호출 종료시, 지역 스택 객체가 사라지므로 의미없다.

힙 기반: delete를 어느 시점에서 종료해야할지 내가 delete를 선언해야하는 issue가 생긴다.


반환할 때는 복사 생성자를 호출하도록 한다.


22. 데이터 멤버가 선언될 곳은 private 영역이다.

왜 데이터는 public이면 안되는가? 

1) 문법적 일관성 : 데이터는 항상 함수로만 접근하게 한다. ex) GetValue(), GetValue 

 함수면 ( )를 붙여야할지 말지 이런 고민이 없어지고 무조건 붙이면 된다.

2) 멤버 접근의 정교한 제어: 어떤 함수는 해당 멤버에 접근, 읽기 전용(const) 등의 접근을 지정할 수 있다.

3) 캡슐화: 캡슐화의 정도는 코드가 깨질확률에 반비례한다.


23. 멤버 함수 보다는 비멤버&비프렌드 함수

어떤 데이터를 접근하는 함수가 많으면 그 데이터의 캡슐화 정도는 낮다

멤버 함수는 클래스의 private 멤버를 전부 건드릴 수 있다(비극!!!) : 캡슐화 정도가 낮다는 소리

그렇다면 프렌드 함수는? 프렌드 함수 또한 private 멤버를 전부 건드릴 수 있다(비극2!!!)

프렌드 함수나 멤버 함수나 그놈이 그놈이라 private 멤버 입장에서는 자신에게 접근할 수 있는 함수가 멤버+프렌드 함수.


비멤버 비프렌드 함수는 어떤 클래스의 private 멤버에 접근하는 함수 개수를 늘리지 않는다. 클래스의 인터페이스 함수만으로 private 멤버의 값을 조작할 수 있다.

-> 진정한 의미의 캡슐화 정도가 높다.


24. type 변환이 모든 매개변수에 대해 적용 되어야 한다면 비멤버 함수를 사용한다.

class Rational{
    int numerator();        // 분자
    int denominator();      // 분모
    const Rational operator *(const Rational& rhs) const;
}

Rational oneHalf(1,2);

Rational result = oneHalf*2;    // ok

Rational result = 2*oneHalf;    // error


명시적 생성자가 없으므로 const Rational temp(2)로 임시 객체를 생성하여 oneHalf*temp를 호출하게 되는것.

명시적 생성자가 있다면 당연히 ok 뜨던것도 오류가 나게된다.

class Rational{
//...
};
const Rational operator* (const Rational &lhs, cconst Rational &rhs){
return 궁시렁궁시렁
}
이때 rational의 public 인터페이스만을 써서 구현했기 때문에 프렌드 함수로 선언 안해도 된다. 멤버 함수의 반대는 프렌드 함수가 아니라 비멤버 함수이다. 어떤 함수에 들어가는 모든 매개변수(this 포인터가 가리키는 객체에 포함해서)에 대해 타입 변환을 해줄 필요가 있다면, 그 함수는 비멤버여야 한다.

25. 예외를 던지지 않는 swap에 대한 지원도 생각해 보자.



'2020 이전 > C++' 카테고리의 다른 글

C++ std::function 콜백  (0) 2019.02.06
C++ auto  (0) 2018.11.28
이펙티브 C++ Chapter 5 구현  (0) 2018.11.12
이펙티브 C++ 챕터3 자원관리 (1회차)  (0) 2018.10.29
이펙티브 C++  (0) 2018.10.28