bdfgdfg

[STL] 1. 함수 객체 사용해보기 본문

게임프로그래밍/STL

[STL] 1. 함수 객체 사용해보기

marmelo12 2021. 12. 22. 17:09
반응형

함수 객체

- 함수 객체는 함수처럼 호출 가능한 클래스 객체.

- 즉 함수처럼 동작하는 객체

- 함수 객체를 정의하는 방법은 '()'연사자를 오버로딩하면 된다.

 

함수 객체의 간단한 예시


class Functor
{
public:
	void operator()(void)
	{
		std::cout << "함수객체 호출!" << std::endl;
	}
};

int main()
{
	Functor f; // 객체생성
	f(); // 오버로딩된 함수 호출
	return 0;
}

물론 매개변수를 넣어줄수도 있다.

class Functor
{
public:
	void operator()(void)
	{
		std::cout << "함수객체 호출!" << std::endl;
	}
	void operator()(int value1,int value2)
	{
		std::cout << "함수객체 인자버전 호출! " << value1 << " " << value2 << std::endl;
	}
};

int main()
{
	Functor f;
	f();
	f(10, 20);
	return 0;
}

이러한 함수 객체의 장점은 함수처럼 동작하는 객체이므로,

다른 멤버 변수,함수들을 가질 수 있으며 일반 함수에서는 불가능한것들이 가능해진다.

또한 속도도 일반 함수보다 함수 객체가 더 빠르다고 한다

 -> 함수의 주소를 전달하여 콜백하는 경우 전달한 함수(함수 포인터)는 inline 될 수 없지만 함수 객체는 인라인 될 수 있고, 컴파일러가 쉽게 최적화가 가능하다.

 

장점 +, 함수 객체이기 때문에 서로 다른 객체의 내부 멤버를 직관적으로 쉽게 수정이 가능하다.

class Adder
{
public:
	Adder() = default;
	virtual ~Adder() = default;
public:
	int operator()(int num) { return m_total += num; }
private:
	int m_total = 0;
};
int main()
{
	Adder add,add2;
	std::cout << add(10) << std::endl;
	std::cout << add2(50) << std::endl;
	return 0;
}

 

또한 STL의 알고리즘에서 마지막 인자로 함수 포인터를 넘겨 사용자 정의에 따른 정렬등도 제공이 되는데

여기서 함수객체도 넘길 수 있다.

#include <iostream>
#include <algorithm> // for_each구문

class Functor1
{
public:
	void operator()(int n) // 공백을 이용하여 원소 출력
	{
		std::cout << n << " ";
	}
};
class Functor2
{
public:
	void operator()(int n) // 각 원소를 제곱 후 공백 출력
	{
		std::cout << n * n << " ";
	}
};
class Functor3
{
public:
	void operator()(int n) // 문자열 +endl을 이용하여 원소 출력
	{
		std::cout << "정수 : " << n << std::endl;
	}
};
int main()
{
	// for_each구문은 범위내 원소들을 넘겨준 함수 포인터,함수 객체,람다등을 통해 처리한다.
	int arr[5] = { 10,20,30,40,50 };
	Functor1 f1;
	Functor2 f2;
	Functor3 f3;
	std::for_each(arr, arr + 5, f1); // 객체를 넘겨준 모습. ()연산자를 오버로딩해야 가능하다!
	std::cout << std::endl;
	std::for_each(arr, arr + 5, f2);
	std::cout << std::endl;
	std::for_each(arr, arr + 5, f3);

	return 0;
}

()연산자가 오버로딩된 클래스의 객체를 마지막 인자로 넘겨줄 시 알아서 ()연산자 오버로딩된 함수를 호출한다.

참고로 객체를 넘기지않고 임시 객체를 넘겨줄수도 있다.

class Functor1
{
public:
	Functor1() { std::cout << "Functor1 객체 생성! \n"; }
public:
	void operator()(int n) // 공백을 이용하여 원소 출력
	{
		std::cout << n << " ";
	}
};
class Functor2
{
public:
	void operator()(int n) // 각 원소를 제곱 후 공백 출력
	{
		std::cout << n * n << " ";
	}
};
class Functor3
{
public:
	void operator()(int n) // 문자열 +endl을 이용하여 원소 출력
	{
		std::cout << "정수 : " << n << std::endl;
	}
};
int main()
{
	// for_each구문은 범위내 원소들을 넘겨준 함수 포인터,함수 객체,람다등을 통해 처리한다.
	int arr[5] = { 10,20,30,40,50 };
	std::for_each(arr, arr + 5, Functor1()); // 임시객체를 생성해 넘겨준다.
	std::cout << std::endl;
	std::for_each(arr, arr + 5, Functor2());
	std::cout << std::endl;
	std::for_each(arr, arr + 5, Functor3());

	return 0;
}

Functor1까지만 확인해보면

임시 객체 생성 후 함수가 호출이 된다.

 

함수 객체 구현

STL에는 유용하게 사용할 수 있는 함수 객체가 내장되어 있다. 

-> 대표적인 함수객체가 바로 less와 greater

-> less는 < 연산자의 함수 객체

-> greater는 > 연산자의 함수 객체이다.

-> 또한 less와 greater는 bool형을 반환하는 조건자(predicate).

 

두 정수를 함수의 매개변수로 넘겨주고 '<' 연산을 수행하는 함수 객체를 본다.

class Less
{
public:
	bool operator()(int a, int b)
	{
		return a < b;
	}
};

int main()
{
	Less test;

	std::cout << test(10, 20) << std::endl; // test 객체를 통한 암묵적 함수 호출
	std::cout << test(20, 10) << std::endl;
	std::cout << Less()(50, 10) << std::endl; // 임시 객체를 통한 암묵적 함수 호출.
	std::cout << Less()(10, 50) << std::endl; 
	std::cout << test.operator()(20,30) << std::endl; // test 객체를 통한 명시적 호출
	std::cout << Less().operator()(30, 20) << std::endl; // 임시 객체를 통한 명시적 호출


	return 0;
}

이렇게 '()'연산자가 오버로딩된 함수를 호출하는 방법도 여러가지가 존재한다.

단 그게 의미하는바가 무엇인지는 알고있어야한다.

 

STL의 less 함수 객체를 이용해보자.

greater 함수 객체또한 functional에 들어있다.

std::less의 멤버를 확인해보면 실제 '()'연산자가 오버로딩된것을 알 수 있다.

int main()
{
	std::less<int> l;

	std::cout << l.operator()(50, 40) << std::endl;
	std::cout << l(20, 30) << std::endl;
	std::cout << std::less<int>()(60, 70) << std::endl;

	return 0;
}

less나 greater와 같은 함수객체를 이용하여, STL의 sort(정렬)나 priority_queue등 정렬을 어떻게할지(오름차순,내림차순)

최대힙인지 최소힙인지를 위의 함수 객체를 건내주면 간편하게 사용할 수 있다.

int main()
{
	int arr[10] = { 5,3,10,20,1,60,100,40,30,67 };
	sort(arr, arr + 10, std::less<int>()); // 오름차순
	for (int i = 0; i < 10; ++i)
		std::cout << arr[i] << " ";
	return 0;
}

prioirty_queue도 마찬가지

int main()
{
	std::priority_queue<int, std::vector<int>, std::greater<int>> pq;

	for (int i = 0; i < 10; ++i)
		pq.push(i + 1);
	for (int i = 0; i < 10; ++i)
	{
		std::cout << pq.top() << std::endl;
		pq.pop();
	}
	return 0;
}

최소힙인 모습.

 

greater는 정반대 내림차순,최대힙.

반응형
Comments