Hyun2and
[Effective C++] 44: 매개변수에 독립적인 코드는 템플릿으로부터 분리시키자 본문
포인트
템플릿으로 코드 비대화가 생긴다. (암시적이기 때문에)
템플릿 매개변수를 함수 매개변수나 멤버 데이터로 넣음으로써 해결할 수 있다.
동일한 이진 구조(long, int) 에 대해서는 한가지 함수 구현(void)을 공유하게 만든다.
정리
코딩 시간 절약, 코드 중복 회피라는 템플릿의 장점을 누리다보면 코드 비대화 (code bloat)가 발생한다.
-> 코드 비대화 : 똑같은 애용의 코드와 데이터가 여러 벌로 중복되어 이진 파일로 구워진다)
코드 비대화를 해결하기 위한 방법
공통성 및 가변성 분석
어렵게 써놓았지만,, 코드 비대화를 해결하기 위해 평소에 코드의 비슷한 부분을 추려서 함수로 빼고, 그 부분이 필요한 곳에 함수들을 호출하도록 바꾸는 과정. 클래스와 템플릿에도 동일한 과정을 거치면 됨
그러나 템플릿은 암시적! 중복되었다는 것을 어떻게 알지? -> 코드를 직접 보고 파악해야 한다
template<typename T, std::size_t n>
class SqureMatrix
{
public:
void invert() {};
};
이런 기본적인 역행렬을 구하는 템플릿이 있다. 비타입 매개변수 n 을 사용한다
SqureMatrix<double, 5> sm1;
sm1.invert();
SqureMatrix<double, 10> sm2; //차이는 n 이 10이라는 것 뿐이다!
sm2.invert();
이 경우 두 sm1, sm2 인스턴스는 크기 외에는 다른 점이 없으나 두번 생성되게 된다
이 코드를 크기 값 n 을 매개변수로 가지도록 수정하여 템플릿으로 만들면 invert 함수는 한 개의 사본만을 가지게 된다
template<typename T>
class SqureMatrixBase
{
//직접 인스턴스를 만들어서 호출하지는 않을 것임. 파생 클래스에서 호출하기 위함
protected:
void invert(std::size_t matrixsize) {};
};
//같은 이유로 private 으로 상속시킨다
template<typename T, std::size_t n>
class SqureMatrix : private SqureMatrixBase<T>
{
public:
//항목 43에서 다루었던 템플릿에서의 호출을 명확하게 하기 위해 this 사용 (or using을 쓰면 됨)
void invert() { this->invert(n); }
};
Base 를 만들어서 기본 클래스를 만드는 경우가 많다
아직 남은 문제... 행렬에 데이터는 어떻게 넘겨줄까?
void invert(std::size_t matrixsize) {};
//이렇게 포인터로 데이터를 넘겨줘도 되지만,, 함수가 n 개 늘어날때마다 다 넣어주는건 좀...
void invert(std::size_t matrixsize, T* matrixptr) {};
암만 생각해도 이쯤 되면 이건 아닌 듯합니다 <
template<typename T>
class SqureMatrixBase
{
protected:
SqureMatrixBase(std::size_t n, T* pMem) :size(n), pData(pMem) {}
void setDataPtr(T* ptr) { pData = ptr; }
void invert(std::size_t matrixsize) {};
private:
// 항목 22 : 데이터 멤버가 선언될 곳은 private 영역임을 명심하자 참고
std::size_t size;
T* pData;
};
이렇게 Base의 멤버함수로 행렬 데이터를 넣어준다
template<typename T, std::size_t n>
class SqureMatrix : private SqureMatrixBase<T>
{
public:
SqureMatrix() : SqureMatrixBase<T>(n, 0), pData(new T[n*n])
{
this->setDataPtr(pData.get());
}
void invert() { this->invert(n); }
private:
boost::scoped_array<t> pData;
};
위의 방법대로 데이터를 파생 클래스에서 저장하고 포인터의 사본을 기본 클래스로 올리는 방법도 있다
아직 boost::scoped_array 를 쓰는 과정에 대해서는 모르겠다. 항목 13(자원 관리에는 객체가 그만) , 55(boost) 참고하라고 함
비교
- 행렬 크기를 지정하여 각각 별도의 인스턴스를 만드는 것
: 컴파일 시점에 행렬 크기가 투입되므로 최적화하기에 좋다
- 크기독립적으로 매개변수 등으로 넘겨 파생클래스들이 공통으로 쓰도록 함
: 한가지 버전의 함수만 존재하므로 실행 코드의 크기가 작아짐 -> 프로그램 작업 세트 크기가 줄어듦 -> 캐시 내의 참조 지역성이 향상됨 -> 프로그램 실행 속도가 더 빨라짐
추가적으로 고려해야할 것
- 객체의 크기 : 크기 독립형 함수들을 기본 클래스에 계속 넣다보면 객체의 전체 크기가 점점 늘어남
- 함수 매개변수 : int, long 을 넘기는 경우는 ? int* / const int* ?
-> T* 포인터를 쓰고, 하단에서 void* 포인터 함수를 호출하도록 동작함
( ex : C++ 표준 라이브러리 vector, deque, list )
+
모르는 게 많아서 정리하는 데에 오래걸린다... 이 방법이 맞는지 계속 고민해보자
'공부엔 끝이없다 > Effective C++' 카테고리의 다른 글
[Effective C++] 46: 타입 변환이 바람직할 경우에는 비멤버 함수를 클래스 템플릿 안에 정의해 두자 (0) | 2023.08.06 |
---|---|
[Effective C++] 45: "호환되는 모든 타입" 을 받아들이는 데는 멤버 함수 템플릿이 직방! (0) | 2023.07.31 |
[Effective C++] 43: 템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법을 알아두자 (1) | 2023.07.23 |
[Effective C++] 1. C++ (0) | 2023.02.19 |
[Effective C++] 0. 서론 (0) | 2023.02.19 |