C++

람다

monstro 2024. 3. 18. 00:37
728x90
반응형

저번 포스팅에서 우리는 함수 객체에 대해 알아보았습니다.

함수 객체는 함수의 동작과 상태를 다룰 수 있다는 점에서 매우 유용하다는 것 역시 알 수 있었습니다.

 

그러나 필요할 때마다 함수 객체를 선언하고 사용하는 것은 매우 불편한 일입니다.

그러한 부분에서 오늘 배월 볼 람다는 일시적으로 사용될 함수 객체를 다루는데에 매우 유용합니다.

 

거두절미하고 한번 알아보겠습니다.

 

구성은 다음과 같습니다.

 

  • 람다의 구성
  • 람다의 기능
  • 람다의 사용
  • 람다의 위험성

 

1)  람다의 구성

람다는 크게 3개의 부분으로 나뉘어 있습니다.

  1. 캡처부
  2. 매개변수부
  3. 구현부

또한 각각의 부분은 다음의 기호로 사용됩니다.

  1. [  ](대괄호)
  2. ( )(소괄호)
  3. { }(중괄호)

 

2)  람다의 기능

람다는 함수 객체를 단발성으로 사용하고자 할 때 사용가능합니다.

그러므로 상태를 포함한다 는 함수 객체의 성질을 어느정도 갖고 있는데,

이는 캡처를 통해 이루어집니다. 그리고 이 캡처는 크게 2가지 -> 작게 4가지로 표현가능합니다.

  • 모든 객체를 캡처하는 기본 캡처 모드
  • 필요한 객체만 캡처하는 단일 변수 캡처 모드

의 2가지로 쪼개지게 됩니다.

 

 

2 - 1)  기본 캡처 모드

(1) = : 모든 객체를 복사하는 캡처(초기값이 영구고정) , 또한 캡처를 비워놓으면 기본적으로 모든 객체를 복사합니다

 

(2) & : 모든 객체를 참조하는 캡처(초기값이 고정되지 않음)

 

 

2 - 2)  단일 변수 캡처 모드

(1) n : 단일 변수 n을 복사

 

(2) &n : 단일 변수 n을 참조

 

(또한 원한다면 , 로 구분하여 2개 이상의 단일 변수를 캡처할 수 있습니다.)

 

2 - 3)  람다의 리턴 타입

또한 람다의 리턴 타입은 기본적으로 컴파일러가 유추하여 결정하지만, 설계자가 직접 지정해 주는 것도 가능합니다.

// 컴파일러는 boolean을 리턴하려 하지만,
[] (Item& item) { return item._rairity == Rarity::Unique; };

// 사용자가 int를 리턴하게끔 설계할 수 있음
[] (Item& item) -> int { return item._rairity == Rarity::Unique; };

 

 

3)  람다의 사용

#include <iostream>
#include <algorithm>
#include <vector>
#include <time.h>
using namespace std;

// 아이템의 타입
enum class ItemType
{
	None,
	Armor,
	Weapon,
	Jewelery,
	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;

	// 벡터에 Common등급 하나, Unique등급 하나 총 2개의 아이템을 집어넣음
	v.push_back(Item(1, Rarity::Common, ItemType::Weapon));
	v.push_back(Item(2, Rarity::Unique, ItemType::Armor));

	// 람다를 통해  Unique 등급 아이템을 탐색
	auto findResult = std::find_if(v.begin(), v.end(), [](Item& item) {return item._rarity == Rarity::Unique; });

	// 탐색되지 않았다면
	if (findResult == v.end())
	{
		cout << "탐색 실패!" << endl;
	}
	// 탐색되었다면
	else
	{
		cout << "탐색 성공!" << endl;
	}
}

 

람다를 통해 함수 객체를 통해 비교해야 하는 동작을 넘겨주는 코드를 한 줄로 표현할 수 있습니다.

실행결과는 

 

다른 예시를 보겠습니다.

 

위와 같이 특정 변수를 캡처하는 방법도 존재합니다.

 

 

4)  람다의 위험성

 

그러나 람다를 사용할 때는 매우 위험한 상황에 처할 수 있습니다.

바로 날라간 메모리에 접근하는 Use-After-Free 문제에 직면할 수 있는 것입니다.

 

이 얘기를 들으면, 참조의 문제가 있을 테니 그렇다면 복사로만 람다를 사용하면 되지 않을까?

라는 생각을 하실 수 있습니다.

 

그러나 람다는 단순복사일때는 this포인터를 사용하여 접근하게 됩니다.

this역시 포인터이므로, 결국 delete된 메모리에 접근하는 문제를 피할 수는 없는 것입니다.

 

 

첫 번째로 호출된 job은 정상적으로 100이 출력되었지만,

두 번째로 호출된 job은 엉뚱한 값이 나와버렸습니다.

 

결국 람다를 사용할때는 반드시 delete된 주소에 접근하지 않도록 조심해야 할 것입니다.

728x90
반응형

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

캐스팅 - C언어의 캐스팅  (0) 2024.04.07
virtual , override 키워드  (0) 2024.03.18
함수 객체  (0) 2024.03.11
함수 포인터  (0) 2024.03.11
멀티바이트와 유니코드  (0) 2024.02.26