궁금증 질문:
(139쪽 참고)
“C++에서는 발에 치이고 손에 잡히는 것이 인터페이스입니다. 함수도 인터페이스요, 클래스도 인터페이스요, 템플릿 또한 인터페이스입니다. 인터페이스는 사용자가 여러분의 코드와 만리장성을 쌓는 접선수단입니다. …”
C++은 C#처럼 별도의 인터페이스라는 것이 없으므로 위에서 설명하는 인터페이스는 어떠한 ‘구조’를 통칭하는 말일까요?
Q. 다음은 클래스 설계에 관련한 명제들입니다. 이 중 거짓인 명제 한 개를 골라주세요 (객관식)
T1
타입의 객체를 T2
타입의 객체로 암시적으로 변환되도록 만들고 싶다면, T1
클래스에 타입 변환 함수를 하나 만드는 방법이 있다. (이를테면 operator T2)대체적으로 효율적일 뿐만 아니라 복사손실 문제까지 막아 준다.
이번 항목에서 다룬 법칙은 기본제공 타입 및 STL 반복자, 그리고 함수 객체 타입에는 맞지 않는다. 이들에 대해서는 '값에 의한 전달'이 더 적절하다.
Q. ‘값에 의한 전달’이 저비용이라고 가정해도 괜찮은 유일한 타입 3가지는 무엇이 있을까요?
기본 제공 타입
STL 반복자
함수 객체 타입
'값에 의한 전달'보다는 '상수객체 참조자에 의한 전달' 방식을 택하는 편이 대개 낫습니다
대체적으로 효율적일 뿐만 아니라 복사손실 문제까지 막아줍니다.
이번 항목에서 다룬 법칙은 기본제공 타입 및 STL 반복자, 그리고 함수 객체 타입에는 맞지 않으며, 이들에 대해서는 '값에 의한 전달'이 더 적절하다고 합니다.
참조자는 그냥 이름이다. 반드시 이미 존재하는 객체에 붙는 다른 이름이다.
[이것만은 잊지 말자]
데이터 멤버를 함수 인터페이스 뒤에 감추게 되면 구현상의 융통성을 가질 수 있다. 그리고 책에서 인상 깊었던 말이 있는데 한번 인용해보겠다.
public이란 '캡슐화되지 않았다'는 뜻이며, 실질적인 측면에서 이는 곧' 바꿀 수 없다'라는 의미를 담고 있다.
즉, 데이터 멤버를 public으로 만들 게 되면 나중에 데이터 멤버를 삭제하거나 변수이름을 변경하거나 타입을 변경하는 등의 작업이 힘들어지게 된다. 만약 라이브러리나 프레임워크 개발이라면 불가능하다고 봐야한다. 따라서 데이터 멤버는 private로 최대한 캡슐화하고 인터페이스 함수를 제공하는게 옳다고 할 수 있다.
[이것만은 잊지 말자]
일반적으로 직관적으로 생각했을 때, 클래스와 연관된 함수들은 클래스의 멤버 함수로 두어야할 것 같다. 하지만 실질적으로 만약 비멤버 비프렌드 함수로 둘 수 있는 경우에는 비멤버 비프렌드 함수로 두는 것이 캡슐화 정도가 높아지고 패키징 유연성이 커진다. 왜냐하면 비멤버 비프렌드 함수는 멤버함수보다 접근권한이 약하고(public만 접근할 수 있음) 해당 클래스와 해당 함수를 파일분할(컴파일 의존성을 줄여준다. 컴파일 시간 감소!) 할 수 있기 때문이다.
class Example {
public:
void doA() {}
void doB() {}
void doC() {}
void doAll() { // Bad!
doA();
doB();
doC();
}
};
void doAll(Example &ex) { // Good!
ex.doA();
ex.doB();
ex.doC();
}
암시적인 형변환을 지원하는 클래스가 있다고 하자. 이 클래스에 대해서 operator을 멤버함수로서 overload했다고 하자. 이경우에는 A B라고 했을 때 A위치에 대해서는 암시적 형변환이 지원되지 않는다. 따라서 이런 경우에는 operator*을 비멤버함수로서 overload해야한다.
[이것만은 잊지 말자]
이 항목은 template에 대한 내용이 많이 등장함으로 이에 대해서 익숙하지 않으신 분들은 책을 읽으면서 헤매셨을수 도 있습니다. 관련 template내용에 대한 보충자료로서 http://egloos.zum.com/sweeper/v/2998778 를 추천합니다. 설명이 깔끔하고 명확하게 잘 되어있습니다. (강추!)
[std namespace에 대한 템플릿 제한사항]
namespace ExampleStuff {
template <typename T>
class ExampleImpl {
};
template <typename T>
class Example {
public:
void swap(Example &ex) {
using std::swap;
swap(pImpl, ex.pImpl);
}
private:
ExampleImpl<T> *pImpl;
};
template <typename T>
void swap(Example<T> a, Example<T> b) {
a.swap(b);
}
}
template <typename T>
void func(T& a, T& b) {
using std::swap;
swap(a, b); // find function by argument-dependent lookup.
}
클래스에 대한 swap을 마련할 생각이라면, 단순히 std::swap에 대한 완전 특수화 함수를 정의하면 된다. 하지만 클래스 템플릿에 대한 swap 특수화 버전을 마련할 때는 다른 방법을 써야한다. (함수 템플릿에 대한 부분 특수화는 불가능하기 때문에) 해결책은 위 코드와 같다. func함수에서 일어나는 일은 다음과 같다. 인자 기반 탐색에 의해 인자 타입과 동일한 namespace에서 swap의 특수화 버전을 먼저 탐색하게 되고, 없을 경우 using에 의해 std namespace에서 탐색하게 된다. 따라서 우리가 원하는 목적을 이룰 수 있다. 문제는 class를 사용하는 사용자가 swap을 사용할 때 func함수와 같이 사용할 것을 기대해야만 한다는 것이다. 사용자가 만약 std::swap()을 사용한다면 다 무용지물이 된다. 그래서 완벽한 방법은 아닌 것 같다... 더 좋은 해결책은 없을까??? (비야네 날 보고있다면 정답을 알려줘!)
[이것만은 잊지 말자]