[디딤돌 C++] 52. 함수 개체

이번에는 함수 호출 연산자 중복 정의와 함수 개체를 알아보기로 해요.

C++언어에서는 함수 호출 연산자를 중복정의 할 수 있습니다. 함수 호출 연산자의 연산 기호는 ( ) 입니다. 따라서 함수 호출 연산자를 중복 정의할 때 메서드 이름은 operator()입니다. 메서드 뒤에 입력 매개 변수 리스트를 열거하는 ()에는 개발자가 개수와 형식을 결정하고 반환 형식도 개발자가 정합니다.

[반환형식] operator() ([입력 매개 변수 리스트]);

그리고 함수 호출 연산자를 중복 정의한 형식의 개체를 함수 개체라고 부릅니다. 함수 개체는 형식 내부에 함수 호출 연산자를 중복 정의하고 있어서 마치 함수처럼 호출하여 사용할 수 있습니다.

먼저 간단하게 함수 호출 연산자 중복 정의를 사용하여 함수 개체를 함수처럼 사용하는 예를 보여드를게요.

//함수 호출 연산자 중복 정의
#include <iostream>
using namespace std;
class FunClass
{
public:
    int operator()(int a,int b)//함수 호출 연산자 중복 정의
    {
        cout<<"테스트"<<endl;
        return a*b;
    }
};
int main()
{
    FunClass fc;//함수 개체

    cout<<fc(3,4)<<endl;//함수 개체를 함수처럼 사용
    return 0;
}

▷ 실행 결과

테스트

12

보통 함수 개체는 메서드를 구현하는 곳에서 모든 알고리즘을 정의하지 못하고 일부를 호출하는 곳에서 정의한 알고리즘을 이용할 때 사용합니다. 예를 들어 회원을 보관하는 배열을 구현하면서 검색 기능을 구현할 때 호출하는 곳에서 검색 기능을 구현한 함수 개체를 입력 인자로 받아 이를 이용하는 것이죠. 또한 배열에 보관한 회원 정보를 출력하는 알고리즘을 호출하는 곳에서 함수 개체로 전달받아 이를 이용하여 출력하게 할 수도 있겠죠. 마찬가지로 배열에 보관한 회원 정보를 정렬할 때 비교 알고리즘을 함수 개체를 입력 인자로 전달받아 이용할 수도 있을 거예요.

다음처럼 배열에서는 함수 호출 연산자를 중복 정의한 알고리즘을 추상화합니다.

#define interface struct
interface IEqual
{
    virtual bool operator()(Member *member)=0;
};

그리고 배열의 검색 기능에서는 함수 개체를 입력 인자로 받아 조건에 맞는 것을 구현하죠.

Member *MemberCollection::FindIf(IEqual &ie)
{
    for(int i=0;i<usage;i++)
    {
        if(ie(base[i]))
        {
            return base[i];
        }
    }
    return 0;
}

사용하는 곳에서는 검색에 필요한 알고리즘을 구체적으로 구현합니다.

class EqualerNum:public IEqual
{
    int num;
public:
    EqualerNum(int num)
    {
        this->num = num;
    }
    virtual bool operator()(Member *member)
    {
        return num == member->GetNum();
    }
};

그리고 배열의 검색 요청 시에 구체적으로 구현한 함수 개체를 입력 인자로 전달하여 원하는 것을 찾습니다.

MemberCollection mc(10);
...중략...
EqualerNum en(3);
Member *member = mc.FindIf(en);
if(member==0)
{
    cout<<"검색 실패"<<endl;
}
else
{
    cout<<"번호:"<<member->GetNum()<<" 이름:"<<member->GetName()<<endl;
}

다음은 함수 개체를 이용하여 회원 컬렉션을 정의하고 이를 이용하는 예제 코드입니다.

//Member.h
#pragma once
#include <iostream>
#include <string>
using namespace std;
class Member
{
    const int num;
    string name;
public:
    Member(int num,string name);
    int GetNum()const; //회원 번호 접근자
    string GetName()const; //회원 이름 접근자
};
//Member.cpp
#include "Member.h"
Member::Member(int num,string name):num(num)
{
    this->name = name;
}
int Member::GetNum()const
{
    return num;
}
string Member::GetName()const
{
    return name;
}
//MemberCollection.h
#pragma once
#include "Member.h"

#define interface struct

interface IEqual//조건 알고리즘 추상화
{
    virtual bool operator()(Member *member)=0;
};
interface IDoSome //회원에 관한 알고리즘 추상화
{
    virtual void operator() (Member *member)=0;
};
interface ICompare//회원 비교 알고리즘 추상화
{
    virtual int operator()(Member *m1,Member *m2)=0;
};
class MemberCollection
{
    Member **base;
    const int max_member;
    int usage;
public:
    MemberCollection(int max);
    ~MemberCollection(void);
    void PushBack(Member *member);//순차 보관
    Member *FindIf(IEqual &ie); //조건에 맞는 개체 탐색
    void ListMember(IDoSome &iv); //전체 회원 데이터에 알고리즘 적용
    void Sort(ICompare &ic); //검색 알고리즘 이용하여 정렬
};
//MemberCollection.cpp
#include "MemberCollection.h"
MemberCollection::MemberCollection(int max):max_member(max)
{
    base = 0;
    if(max)
    {
        base = new Member *[max];
    }
    usage = 0;
}
MemberCollection::~MemberCollection(void)
{
    if(base)
    {
        delete[] base;
    }
}

void MemberCollection::PushBack(Member *member)
{
    if(usage<max_member)
    {
        base[usage] = member;
        usage++;
    }
}

Member *MemberCollection::FindIf(IEqual &ie)
{    
    for(int i=0;i<usage;i++)
    {
        if(ie(base[i])) //조건 알고리즘을 사용
        {
            return base[i];
        }
    }
    return 0;
}

void MemberCollection::ListMember(IDoSome &iv)
{
    for(int i=0;i<usage;i++)
    {
        iv(base[i]);//회원에 관한 알고리즘 사용
    }
}

void MemberCollection::Sort(ICompare &ic)
{
    for(int i=usage;i>1;i--)
    {
        for(int j=1;j<i;j++)
        {
            if(ic(base[j-1],base[j])>0) //비교 알고리즘 사용
            {
                Member *temp = base[j-1];
                base[j-1] = base[j];
                base[j]=temp;
            }
        }
    }
}
//Program.cpp
#include "MemberCollection.h"

class EqualerNum:public IEqual //번호로 비교 알고리즘
{
    int num;
public:
    EqualerNum(int num)
    {
        this->num = num;
    }
    virtual bool operator()(Member *member)
    {
        return num == member->GetNum();
    }
};

class ViewMember:public IDoSome//회원 출력 알고리즘
{
public:
    virtual void operator() (Member *member)
    {
        cout<<"번호:"<<member->GetNum()<<" 이름:"<<member->GetName()<<endl;
    }
};


class ViewMember2:public IDoSome//회원 출력 알고리즘2
{
public:
    virtual void operator() (Member *member)
    {
        cout<<"이름:"<<member->GetName()<<" 번호:"<<member->GetNum()<<endl;
    }
};

class Dispose:public IDoSome////회원에 관한 소멸 알고리즘
{
    public:
    virtual void operator() (Member *member)
    {
        delete member;
    }
};

class CompareByNum:public ICompare//번호로 비교 알고리즘
{
public:
    virtual int operator()(Member *m1,Member *m2)
    {
        return m1->GetNum()-m2->GetNum();
    }
};

class CompareByName:public ICompare//이름으로 비교 알고리즘
{
public:
    virtual int operator()(Member *m1,Member *m2)
    {
        string s1 = m1->GetName();
        string s2 = m2->GetName();
        return s1.compare(s2);
    }
};

int main()
{
    MemberCollection mc(10);

    mc.PushBack(new Member(5,"이순신"));
    mc.PushBack(new Member(3,"홍길동"));
    mc.PushBack(new Member(1,"강감찬"));    

    EqualerNum en(3);//번호로 검색 알고리즘 함수 개체
    Member *member = mc.FindIf(en);//입력 인자로 함수 개체 전달
    if(member==0)
    {
        cout<<"검색 실패"<<endl;
    }
    else
    {
        cout<<"번호:"<<member->GetNum()<<" 이름:"<<member->GetName()<<endl;
    }
    
    cout<<"전체 출력1"<<endl;
    ViewMember vm;//회원 출력 알고리즘 함수 개체
    mc.ListMember(vm);//입력 인자로 함수 개체 전달

    cout<<"전체 출력2"<<endl;
    ViewMember2 vm2;//회원 출력 알고리즘2 함수 개체
    mc.ListMember(vm2);//입력 인자로 함수 개체 전달

    cout<<"번호로 정렬 후 출력"<<endl;
    CompareByNum cbnum;//번호로 비교 알고리즘 함수 개체
    mc.Sort(cbnum);//입력 인자로 함수 개체 전달
    mc.ListMember(vm);//입력 인자로 함수 개체 전달

    cout<<"이름으로 정렬 후 출력"<<endl;
    CompareByName cbname;//이름으로 비교 알고리즘 함수 개체
    mc.Sort(cbname);//입력 인자로 함수 개체 전달
    mc.ListMember(vm2);//입력 인자로 함수 개체 전달


    Dispose dispose;//소멸 알고리즘 함수 개체
    mc.ListMember(dispose);//입력 인자로 함수 개체 전달

    return 0;
}

▷ 실행 결과

번호:3 이름:홍길동

전체 출력1

번호:5 이름:이순신

번호:3 이름:홍길동

번호:1 이름:강감찬

전체 출력2

이름:이순신 번호:5

이름:홍길동 번호:3

이름:강감찬 번호:1

번호로 정렬 후 출력

번호:1 이름:강감찬

번호:3 이름:홍길동

번호:5 이름:이순신

이름으로 정렬 후 출력

이름:강감찬 번호:1

이름:이순신 번호:5

이름:홍길동 번호:3