스토리텔링 개발자

[Effective STL] 38. 함수 객체 클래스 값 전달(pass by value) 고려하기 본문

개발/Effective STL

[Effective STL] 38. 함수 객체 클래스 값 전달(pass by value) 고려하기

김디트 2025. 1. 3. 10:38
728x90

항목 38. 함수 객체 클래스는 값으로 전달되도록(pass-by-value) 설계하자

 

 

 

매개변수로서의 함수와 함수 객체
  • 함수는 포인터 형태로 매개변수로 넘겨진다.
void qsort(void* base, size_t nmemb, size_t size, int (*cmpfcn)(const void*, const void*));
// cmpfcn을 자세히 살펴보면 값으로 전달되고 있음을 알 수 있다.
// 즉, 함수 포인터는 값으로 전달된다.
  • STL 함수 객체는 이 함수 포인터를 본떴으므로, 동일하게 값으로 전달된다.
template<typename InputIterator, typename Function>
Function for_each(InputIterator first, InputIterator last, Function f);
// Function은 값 전달 / 값 반환이 이루어짐을 알 수 있다.
  • 물론 '반드시' 값으로 전달됨을 보장하진 않는다. 아래처럼 사용할 수 있기 때문이다.
class DoSomething
{
    void operator()(int x) { ... }
    ...
};

using DequeIntIter = deque<int>::iterator;
deque<int> di;
...
DoSomething d;
...
// DoSomething& 을 매개변수로 넘겼기에 참조 전달과 반환으로 동작한다.
for_each<DequeIntIter, DoSomething&>(di.begin(), di.end(), d);
  • 하지만, 값으로 전달되는 것을 기본 전제로 깔고 가는 것이 맞다.

 

 

함수 객체를 제대로 동작시키는 일은 사용자의 몫
  • 이 말의 의미는...
    1. 복사 동작중의 비용을 줄이기 위해 함수 객체를 최대한 작게 만들자.
    2. 가상 함수를 없게 구성하여, 함수 객체를 단형성으로 만들자.
      • 값 복사는 슬라이스 문제가 발생할 수 있다.
  • 물론 위를 완전히 지키는 것은 사실상 비현실적이다.
    1. 상태 저장을 위해 함수 객체가 비대화될 수밖에 없는 상황들이 발생할 것이다.
    2. 구현 상속과 동적 바인딩을 포기하고 클래스를 설계하라는 말인데.. 너무나 비효율적이다.

 

 

 

'함수 객체를 값 전달할 것' 규칙을 어기지 않고 잘 동작하는 함수 객체 만들기
  • 함수 객체 클래스에 넣고자 하는 데이터나 가상함수를 추출하여 다른 클래스로 옮긴다.
  • 그리고 함수 객체 클래스에 그 다른 클래스의 포인터를 삽입한다.
template<typename T>
class BPFC // Big Polymorphic Functor Class(항목 40 참조)
{
private:
    // 값이 비대하다.
    Widget w;
    int x;
    ...
public:
    virtual void operator()(const T& val) const; // 가상함수를 가진다.
    ...
};
  • 이를 pImpl 상용구를 사용하여 아래처럼 바꾼다.
template<typename T>
class BPFCImpl
{
private:
    Widget w;
    int x;
    ...
    virtual ~BPFCImpl();
    virtual void operator()(const T& val) const;
    
    friend class BPFC<T>;
};

template<typename T>
class BPFC
{
private:
    BPFCImpl<T>* pImpl; // 값은 pImpl 포인터 뿐이다.
public:
    void operator()(const T& val) const // 이제 가상함수가 아니다.
    {
        pImpl->operator()(val);
    }
...
};
  • 주의할 점은, 함수 객체 클래스는 객체 복사에도 적절히 동작해야 한다는 것이다.
  • 즉, 함수 객체 구성 시엔, BPFC의 복사 생성자를 어떻게 구현하느냐가 관건이다.

 

728x90
Comments