람다(Lambda)는 이름 없는 함수 객체로 함수를 변수처럼 다룰 수 있게 해줌
// 기본 문법
[캡처](매개변수) -> 반환타입 { 함수 본문 }
// 예시
auto add = [](int a, int b) -> int { return a + b; };
int result = add(3, 5); // 8
람다 외부의 변수를 람다 내부에서 사용할 수 있게 가져오는 것
int x = 10;
int y = 20;
// 값으로 캡처 (복사)
auto lambda1 = [x]() { return x * 2; };
// 참조로 캡처
auto lambda2 = [&x]() { x = 100; };
// 여러 변수 캡처
auto lambda3 = [x, &y]() { y = x + 1; };
| 캡처 방식 | 설명 | 예시 |
|---|---|---|
[x] | x를 값으로 캡처 (복사) | 원본 변경 안 됨 |
[&x] | x를 참조로 캡처 | 원본 변경됨 |
[=] | 모든 변수를 값으로 캡처 | 전역 캡처 |
[&] | 모든 변수를 참조로 캡처 | 전역 캡처 |
[this] | 현재 객체의 this 포인터 캡처 | 멤버 접근 가능 |
void processData() {
int Value = 100;
int temp = 0; // 임시 변수
int debugCounter = 0; // 디버깅용
std::string logMessage = ""; // 로그용
// [=] 사용, 모든 변수가 캡처됨
auto lambda = [=]() {
return Value * 2;
};
// 실제로 필요한 건 Value 하나뿐인데
// temp, debugCounter, logMessage까지 모두 복사됨
// 메모리 낭비
}
std::function<int()> createLambda() {
int localValue = 2;
// [&] 참조 캡처
return [&]() {
return localValue; // localValue는 이미 파괴됨
};
}
void danger() {
auto lambda = createLambda();
int result = lambda(); // 정의되지 않은 동작 (Undefined Behavior)
}
class Widget {
int m_id = 1;
void setupCallback() {
// [=]는 this를 캡처함
auto callback = [=]() {
return m_id; // this->m_id 접근
};
// Widget이 파괴되면 callback은 댕글링 this를 가짐
}
};
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 위험
GetWorld()->GetTimerManager().SetTimer(
TimerHandle,
[this]()
{
// 3초 후 실행되는데 이 시점에 Actor가 이미 파괴되었다면 크래시
DoSomething();
},
3.0f,
false
);
}
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// TWeakObjectPtr로 약한 참조 생성
TWeakObjectPtr<AMyActor> WeakThis(this);
GetWorld()->GetTimerManager().SetTimer(
TimerHandle,
[WeakThis]()
{
// 유효성 검사
if (WeakThis.IsValid())
{
WeakThis->DoSomething();
}
},
3.0f,
false
);
}
객체가 파괴되면 자동으로 타이머가 실행되지 않음
void AMyActor::BeginPlay()
{
Super::BeginPlay();
FTimerDelegate TimerDelegate = FTimerDelegate::CreateWeakLambda(
this, // UObject를 상속받은 객체
[this]()
{
// this가 유효할 때만 실행됨
DoSomething();
}
);
GetWorld()->GetTimerManager().SetTimer(
TimerHandle,
TimerDelegate,
3.0f,
false
);
}
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 간결한 방법
GetWorld()->GetTimerManager().SetTimer(
TimerHandle,
FTimerDelegate::CreateWeakLambda(this, [this]()
{
DoSomething();
}),
3.0f,
false
);
}
델리게이트에 람다를 바인딩할 때 사용
// 싱글캐스트 델리게이트
DECLARE_DELEGATE(FMyDelegate);
void AMyActor::SetupDelegate()
{
FMyDelegate MyDelegate;
// BindWeakLambda 사용
MyDelegate.BindWeakLambda(this, [this]()
{
// this가 유효할 때만 실행됨
HandleCallback();
});
}
// 멀티캐스트 델리게이트
DECLARE_MULTICAST_DELEGATE(FMyMulticastDelegate);
FMyMulticastDelegate OnSomethingHappened;
void AMyActor::BindToDelegate()
{
OnSomethingHappened.AddWeakLambda(this, [this]()
{
HandleEvent();
});
}
| 메서드 | 용도 | 사용 예시 |
|---|---|---|
FTimerDelegate::CreateWeakLambda | 타이머 델리게이트 생성 | 타이머 콜백 |
BindWeakLambda | 싱글캐스트 델리게이트 바인딩 | 일반 델리게이트 |
AddWeakLambda | 멀티캐스트 델리게이트에 추가 | 이벤트 구독 |
TWeakObjectPtr + IsValid() 체크 | 범용 | 다른 Actor 참조 |