개요
C++에서 람다로 복사 캡쳐 하여 클래스의 멤버 변수를 사용하면 멤버 변수만 복사되어 넘어오는 것이 아니고, 클래스 그 자체가 복사된다.
C#에서는 async의 매직 코드로 멤버 변수만 넘어왔었는데, 이 사실을 모르고 C++에서도 비슷하게 사용했다가 복사로 인한 부하가 심각하게 발생하는 문제가 발생하였었다. -_-;;
코드
#include <iostream>
class CMember
{
public:
CMember() { std::cout << "CMember 기본 생성자" << std::endl; }
CMember(const CMember& _other) { std::cout << "CMember 복사 생성자" << std::endl; }
};
class CMain {
public:
CMain() { std::cout << "CMain 기본 생성자" << std::endl; }
CMain(const CMain& _other) { std::cout << "CMain 복사 생성자" << std::endl; }
CMember m_Member;
};
void MyFunction(const CMain& _main) {}
int main(int argc, const char* argv[])
{
CMain main_class;
std::cout << "**** 람다 생성 ****" << std::endl;
auto func = [=]() -> void { MyFunction(main_class); };
return 0;
}
/*
CMember 기본 생성자
CMain 기본 생성자
**** 람다 생성 ****
CMember 기본 생성자
CMain 복사 생성자
*/
자 이렇게 main_class를 복사 캡쳐 모드에서 사용하면 main_class가 복사되어 복사 생성자가 당연히 호출되는 것을 볼 수 있다.
그렇다면 멤버 변수를 사용하면 어떨까?
#include <iostream>
class CMember
{
public:
CMember() { std::cout << "CMember 기본 생성자" << std::endl; }
CMember(const CMember& _other) { std::cout << "CMember 복사 생성자" << std::endl; }
};
class CMain {
public:
CMain() { std::cout << "CMain 기본 생성자" << std::endl; }
CMain(const CMain& _other) { std::cout << "CMain 복사 생성자" << std::endl; }
CMember m_Member;
};
void MyFunction(const CMember& _member) {}
int main(int argc, const char* argv[])
{
CMain main_class;
std::cout << "**** 람다 생성 ****" << std::endl;
auto func = [=]() -> void { MyFunction(main_class.m_Member); };
return 0;
}
/*
CMember 기본 생성자
CMain 기본 생성자
**** 람다 생성 ****
CMember 기본 생성자
CMain 복사 생성자
*/
이렇게 멤버 변수를 사용하도록 바꿨음에도 불구하고 여전히 CMain이 복사되어 사용되는 것을 볼 수 있다.
그래서 거대 클래스의 멤버 변수를 복사 캡쳐 할 때에는 반드시 주의가 필요하다.
#include <iostream>
class CMember
{
public:
CMember() { std::cout << "CMember 기본 생성자" << std::endl; }
CMember(const CMember& _other) { std::cout << "CMember 복사 생성자" << std::endl; }
};
class CMain {
public:
CMain() { std::cout << "CMain 기본 생성자" << std::endl; }
CMain(const CMain& _other) { std::cout << "CMain 복사 생성자" << std::endl; }
CMember m_Member;
};
void MyFunction(const CMember& _member) {}
int main(int argc, const char* argv[])
{
CMain main_class;
std::cout << "**** 람다 생성 ****" << std::endl;
CMember _member = main_class.m_Member;
auto func = [=]() -> void { MyFunction(_member); };
return 0;
}
/*
CMember 기본 생성자
CMain 기본 생성자
**** 람다 생성 ****
CMember 복사 생성자
CMember 복사 생성자
*/
CMain의 불필요한 복사를 막고 CMember만의 온전한 복사를 하려면 위와 같이 코드를 수정해야 할 것이다.
주절
C++의 캡쳐에서는 모호한 표현(main_class를 복사할 것인가? m_Member만 복사할 것인가?, l, r value는 어떻게 할 것이지?)을 최소화하기 위해서 표현식(main_class.m_Meber)을 캡쳐하는 것을 금하고, 로컬 변수명(main_class)만을 캡쳐 가능하도록 디자인하였다고 한다.
그래서 멤버 변수를 복사 생략 캡쳐하여 사용하면 로컬 변수인 main_class를 캡쳐한다음 멤버 변수로 꺼내 쓰는 방식으로 동작하게 되는 것이다.
여기서 모호함이 다시 생겨나는데 실수하지 않도록 반드시 주의가 필요할 것 같다.
'Programming > C++ & Unreal' 카테고리의 다른 글
메모리 파편화 + boost, tbb 도구 (0) | 2025.03.30 |
---|---|
C++ 스마트 포인터 메모리는 shared_ptr가 없어져도 해제되지 않는다. (1) | 2025.03.30 |
vprintf, vsprintf 등 가변 인자 함수를 활용한 로그 함수 개발 시에 포맷 warning 출력하기 (0) | 2024.04.05 |
StackWalk64가 context에 의하여 Access violation를 유발 (1) | 2023.10.23 |
C++ 가변인자(va_list)를 오버로딩 하지 말자. (1) | 2023.10.23 |