저번 포스팅에서 우리는 함수 객체에 대해 알아보았습니다.
함수 객체는 함수의 동작과 상태를 다룰 수 있다는 점에서 매우 유용하다는 것 역시 알 수 있었습니다.
그러나 필요할 때마다 함수 객체를 선언하고 사용하는 것은 매우 불편한 일입니다.
그러한 부분에서 오늘 배월 볼 람다는 일시적으로 사용될 함수 객체를 다루는데에 매우 유용합니다.
거두절미하고 한번 알아보겠습니다.
구성은 다음과 같습니다.
- 람다의 구성
- 람다의 기능
- 람다의 사용
- 람다의 위험성
1) 람다의 구성
람다는 크게 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된 주소에 접근하지 않도록 조심해야 할 것입니다.
'C++' 카테고리의 다른 글
캐스팅 - C언어의 캐스팅 (0) | 2024.04.07 |
---|---|
virtual , override 키워드 (0) | 2024.03.18 |
함수 객체 (0) | 2024.03.11 |
함수 포인터 (0) | 2024.03.11 |
멀티바이트와 유니코드 (0) | 2024.02.26 |