본문 바로가기
프로그래밍 이야기/C++ 기초

[C++] 일반 함수에서의 가변성

by Mulder5 2020. 12. 11.
반응형

[문제점] 다음과 같이 정렬을 수행하는 예제가 있다. Sort함수의 정책(오름차순 정렬, 내림차순 정렬)을 외부에서 결정할 수 있도록 고쳐보자.

#include <iostream>
#include <algorithm>

using namespace std;

void Sort(int* x, int size)
{
    for (int i = 0; i < size-1; i++)
    {
        for (int j = i+1 ; j< size; j++)
        {
            // 오름차순, 내림차순의 정책을 결정하는 코드
            if( x[i] < x[j] )
            {
                swap(x[i], x[j]);
            }
        }
    }
}

int main()
{
    int x[10] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
    Sort(x, 10);
    for(auto n : x)
    {
        cout<< n << ", ";
    }

    return 0;
}

이 예제에서 사용자가 이 정책을 변경할 수 있도록 하려면, 우선 습관적으로 변하는 것과 변하지 않는 것을 구분하자. 변하는 것을 가상함수나 다른 클래스로 분리할 수 있었다. 하지만 이 예제는 일반 함수. 일반 함수에서도 역시 이 법칙에 의해  변하는 것(정책)을 함수로 인자화 하여 수정할 수 있다.

void Sort2(int* x, int size, bool(*cmp)(int, int))
{
    for (int i = 0; i < size-1; i++)
    {
        for (int j = i+1 ; j< size; j++)
        {
            // 오름차순, 내림차순의 정책을 결정하는 코드
            if( cmp(x[i], x[j]) )
            {
                swap(x[i], x[j]);
            }
        }        
    }    
}

bool cmp1(int a, int b) { return a < b; }
bool cmp2(int a, int b) { return b > a; }


int main()
{
    int x[10] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
    
    Sort2(x, 10, &cmp1);
    for(auto n : x)
    {
        cout<< n << ", ";
    }
    return 0;
}

위와 같이 함수를 Sort함수의 인자로 사용할 수 있다. 즉, 함수 포인터를 사용할 수 있고, 이 인제에 cmp1 또는 cmp2의 주소를 전달하여 정책이 결정될 수 있다. 이러한 처리 방식은 qsort()에서 볼 수 있다.

하지만 이렇게 함수 포인터를 사용하게 되면 인라인 치환이 안됭어서 성능상 불리 할 수 있다. 이를 해결하기 위해서 람다식을 사용할 수 있다.

template<typename T>
void Sort3(int* x, int size, T cmp)
{
    for (int i = 0; i < size-1; i++)
    {
        for (int j = i+1 ; j< size; j++)
        {
            // 오름차순, 내림차순의 정책을 결정하는 코드
            if( cmp(x[i], x[j]) )
            {
                swap(x[i], x[j]);
            }
        }        
    }    
}

bool cmp1(int a, int b) { return a < b; }
bool cmp2(int a, int b) { return b > a; }

int main()
{
    int x[10] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
   
    Sort3(x, 10, [](int a, int b) {return a > b; });
    for(auto n : x)
    {
        cout<< n << ", ";
    }
    return 0;
}

람다와 함수 포인터의 장/단점을 아래와 같이 정리해볼 수 있겠다.

함수포인터를 사용할 경우 : 코드 메모리가 증가하지 않지만 인라인 치환이 안된다.
람다를 사용할 경우 : 인라인 치환이 되지만 코드 메모리가 증가 한다.

반응형