오븐 노트

[C++] 람다 (lambda) 본문

Develop/C++

[C++] 람다 (lambda)

오 븐 2024. 1. 2. 21:02
#include <iostream>
using namespace std;
#include <vector>
#include <list>
#include <deque>
#include <map>
#include <set>
#include <algorithm>

// 람다(lambda) -> 함수 객체를 빠르게 만드는 문법

enum class ItemType
{
	None,
	Armor,
	Weapon,
	Jewelry,
	Consumable
};

enum class Rarity
{
	Common,
	Rare,
	Unique
};

class Item
{
public:
	Item() { }
	Item(int itemId, Rarity rarity, ItemType type)
		: _itemId(itemId), _rarity(rarity), _type(type)
	{

	}

public:
	int _itemId = 0;
	Rarity _rarity = Rarity::Common;
	ItemType _type = ItemType::None;
};

int main()
{
	vector<Item> v;
	v.push_back(Item(1, Rarity::Common, ItemType::Weapon));
	v.push_back(Item(2, Rarity::Common, ItemType::Armor));
	v.push_back(Item(3, Rarity::Rare, ItemType::Jewelry));
	v.push_back(Item(4, Rarity::Unique, ItemType::Weapon));

	// 람다 = 함수 객체를 손쉽게 만드는 문법
	// 람다 자체로 C++11에 '새로운' 기능이 들어간 것은 아니다.
	{
		struct FindItem
		{
			FindItem(int itemId, Rarity rarity, ItemType type)
				: _itemId(itemId), _rarity(rarity), _type(type)
			{

			}

			bool operator()(Item& item)
			{
				return item._itemId == _itemId && item._rarity == _rarity && item._type == _type;
			}

			int _itemId;
			Rarity _rarity;
			ItemType _type;
		};

		// 클로저 (closure) = 람다에 의해 만들어진 실행시점 객체
		// auto isUniqueLambda = [](Item& item) { return item._rarity == Rarity::Unique; }; // 람다 표현식(lambda expression)
		
		int itemId = 4;
		Rarity rarity = Rarity::Unique;
		ItemType type = ItemType::Weapon;

		// [ ] 캡처(capture) : 함수 객체 내부에 변수를 저장하는 개념과 유사
		// 기본 캡처 모드 : 값(복사) 방식(=) / 참조 방식(&)
		//auto findByItemIdLambda = [&](Item& item) { return item._itemId == itemId; };
		// 변수마다 캡처 모드를 지정해서 사용 가능 : 값 방식 (name), 참조 방식 (&name) // 기본 캡처 모드보다 해당 방식을 추천!!!
		auto findByItem = [itemId, rarity, type](Item& item) // 실무에서는 방식을 통일하여 부호로 작성하는 방식을 지양하는것이 좋다(직접 하나하나 작성). 꼼꼼히 뜯어보지 않는다면, 어떤게 어디로 들어갔는지 확인하기 매우 어려워진다, 가독성 이슈
		{ // 만약 &itemId 처럼 주소값을 넘기게 된다면, 해당 주소값은 더 이상 사용해서는 안되는 상황이 발생 할 수도 있기에 람다 캡처를 할 경우에는 굉장히 조심하여 사용하여야한다.
			return item._itemId == itemId && item._rarity == rarity && item._type == type;
		}; // 사실, 람다 객체를 외부로 넘기는게 아니라면 기본 캡처 모드로 설정한다고 크게 문제 될 부분은 없긴하다.

		itemId = 10;

		auto findIt = std::find_if(v.begin(), v.end(), FindItem(4, rarity, type)); // 람다를 일회성으로 사용하려면 내용 자체를 박아넣어도 되긴 한다
		// 이러한 stl과 같이 마지막 인자로 functor나 predicate, 함수 포인터 등등을 받아주는 형태에서는 무조건 lambda와 함께 동작하도록 하는것이 코드 가독성에도 좋고 작업 속도도 훨씬 빨라진다
		if (findIt != v.end())
			cout << "아이템ID: " << findIt->_itemId << endl;
	}

	{
		class Knight
		{
		public:
			auto ResetHpJob()
			{ // 람다 자체는 타입에 대한 이름이 지어지지 않다보니 뱉어줄 때 어떤 형식으로 해야할지 매우 애매한데 auto(자동추론) 활용.
				auto f = [this]() // 사실상 여기서 []는 [this]와 같음 / this 넣어준 이유는 가독성
				{ // 모든 캡처 자체를 기본적으로 복사하는 것으로 만들었다고 하더라도 "모든것이 안전하게 동작한다는 보장은 절대없다!"
					this->_hp = 200; // 어떠한 클래스 내부에 존재하는 함수에서는 멤버 변수에 접근 할 경우, 항상 this포인터를 이용하여 접근하고 있는것. 때문에 생략 가능
				};

				return f;
			}

		public:
			int _hp = 100;
		};

		class Functor
		{
		public:
			Functor(Knight* k) : _knight(k)
			{

			}

			void operator()()
			{
				_knight->_hp = 200;
			}
		public:
			Knight* _knight;
		};

		Knight* k = new Knight();
		auto jop = k->ResetHpJob(); // 해당 jop 자체는 함수 객체이다 보니, 원할 때 실행 할 수 있음
		delete k; // this->_hp = 200; 부분에서 이미 날아간 메모리를 건드리고 있다. 메모리 오염
		jop();
	}

	// lambda 기본 형태 : []() {} : [캡처(functor에 내부 변수로 저장할 변수들을 복사할지, 참조값으로 넣을지 지정](인자값) { 구현부 }

	return 0;
}

[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 - 인프런 | 강의
 
C++ 카테고리의 글은 인프런 Rookiss님의 강의를 공부하며 정리하는 내용입니다.
이미 알고 있는 내용도 다시 정리 되어있을 수 있습니다.

 

모든 글은 제가 공부하기 위해 작성합니다.

'Develop > C++' 카테고리의 다른 글

[C++] 스마트 포인터 (smart pointer)  (0) 2024.01.04
[C++] 전달 참조 (forwarding reference)  (0) 2024.01.02
[C++] 오른값 참조 (rvalue reference)  (0) 2023.12.30
[C++] override, final  (0) 2023.12.28
[C++] delete(삭제된 함수)  (0) 2023.12.13