[C++] Resource Move

chooha·2025년 10월 19일

C++

목록 보기
10/22

📚 C++ 매개변수 전달과 복사 최적화

🔸 Pass by Value vs Pass by Pointer vs Pass by Reference

세 가지 전달 방식의 특징

  • Pass by Value : 값을 복사해서 전달함. 원본 데이터는 변경되지 않음
  • Pass by Pointer : 메모리 주소를 전달함. 포인터 연산이 가능하고, nullptr 체크가 필요함
  • Pass by Reference : 참조를 통해 전달함. 포인터처럼 동작하지만 더 안전하고 편리함

성능 비교

  • Pointer와 Reference는 어셈블리 수준에서 동일하게 동작함
  • 큰 객체를 전달할 때는 복사 비용이 큰 Pass by Value보다 Reference나 Pointer를 사용하는 것이 효율적임
  • 포인터가 꼭 필요한 상황(nullptr 체크, 포인터 연산 등)이 아니라면 Reference를 사용하는 것이 더 안전하고 편리함

🔸 L-value와 R-value

L-value (Left value)

  • 메모리 상에 위치가 있어서 여러 번 참조할 수 있는 값
  • 변수명으로 식별 가능한 객체
  • 주소 연산자(&)를 사용할 수 있음
  • 예시: int x = 10;에서 x는 L-value

R-value (Right value)

  • 임시로 생성되어 표현식이 끝나면 사라지는 값
  • 메모리 주소를 가질 수 없음
  • 대입 연산자의 오른쪽에만 올 수 있음
  • 예시: int x = 10;에서 10은 R-value, x + 5도 R-value

🔸 L-value Reference vs R-value Reference

L-value Reference (&)

  • L-value를 참조하기 위한 방식
  • 기존 변수를 참조할 때 사용
  • 선언: string& ref = str;

R-value Reference (&&)

  • R-value를 참조하기 위한 방식 (C++11부터 도입)
  • 임시 객체나 이동 가능한 객체를 받을 때 사용
  • Move semantics를 구현하기 위해 사용됨
  • 주로 함수 매개변수로 활용: void func(string&& s)

Move Semantics란?

  • 객체의 자원(메모리 등)을 복사하지 않고 이동시키는 최적화 기법
  • 불필요한 복사를 줄여 성능을 향상시킴
  • R-value reference를 통해 구현됨

🔸 예제 코드: 매개변수 전달 방식

void StoreByValue(string s)
{ 
	string b = s;
}
void StoreByLRef(string& s)
{ 
	string b = s; 
}
void StoreByRRef(string&& s)
{ 
	//string b = s;
    string b = std::move(s);
}
int main()
{
	string a = "abc";
	StoreByValue(a);
	StoreByLRef(a);
	StoreByRRef("abc");
    
	return 0;
}

🔸 std::move를 통한 복사 최적화

std::move의 역할

  • L-value를 R-value로 캐스팅하여 move semantics를 활용할 수 있게 해줌
  • 불필요한 복사를 제거하고 이동으로 대체하여 성능을 향상시킴
  • 객체의 소유권을 이전할 때 사용함

예제 코드: std::move 활용

class Cat
{
	public:
		void SetName(string name)
		{ 
        	mName = std::move(name);
        }
	private:
		string mName;
};

int main()
{
	Cat kitty;
	string s = "kitty";
	kitty.SetName(s); // 1 copy
	kitty.SetName("nabi"); // 0 copy
}

복사 횟수 분석

  • kitty.SetName(s) : L-value 전달 → 매개변수로 1 copy, std::move로 이동 → 총 1 copy
  • kitty.SetName("nabi") : R-value 전달 → 매개변수로 이동, std::move로 이동 → 총 0 copy

🔸 RVO (Return Value Optimization)

RVO란?

  • 컴파일러가 함수의 반환 과정에서 발생하는 불필요한 복사를 제거하는 최적화 기법
  • C++17부터는 특정 조건에서 RVO가 의무화됨 (guaranteed copy elision)
  • 반환 값을 임시 객체로 만들지 않고 직접 목적지에 생성함

예제 코드: RVO 적용

string getString()
{
	string s = "hello";
	return s;
}

int main()
{
	string a = getString();
	return 0;
}

동작 방식

  • 반환할 때 1 copy가 이루어질 것 같지만, RVO의 개입으로 s는 만들어지지 않고 바로 a에 hello가 저장되어 0 copy가 가능함

RVO가 개입되지 않는 경우

string getString(string s)
{
	if(조건)
	{
		s = "hello";
	}
	return s;
}
  • s에 바로 hello를 대입해도 되는지 모르는 상황이므로 RVO 개입이 불가능함
  • 그래도 0 copy로 이루어지는데, 이는 move constructor 덕분임 (이건 나중에 정리)

NRVO (Named RVO)

  • 이름이 있는 지역 변수를 반환할 때의 최적화
  • 컴파일러가 보장하지 않으며, 조건에 따라 적용 여부가 달라짐
  • 여러 반환 경로가 있거나 조건부 반환이 있으면 적용되지 않을 수 있음

🔸 사용 가이드라인

매개변수 전달 방식 선택
1. Pass by Value

  • 작은 타입(int, char 등)을 전달할 때
  • 복사본이 필요할 때
  1. Pass by Const Reference (const T&)

    • 큰 객체를 읽기 전용으로 전달할 때
    • 가장 일반적으로 권장되는 방식
  2. Pass by Reference (T&)

    • 함수 내에서 원본을 수정해야 할 때
  3. Pass by R-value Reference (T&&)

    • Move semantics를 활용할 때
    • 임시 객체를 효율적으로 처리할 때
    • Perfect forwarding을 구현할 때
  4. Pass by Pointer (T*)

    • nullptr 가능성이 있을 때
    • 포인터 연산이 필요할 때
    • C 라이브러리와 호환이 필요할 때

최적화 활용

  • 멤버 변수에 값을 저장할 때는 std::move()를 사용하여 불필요한 복사를 줄일 수 있음
  • 함수 반환 시에는 RVO를 믿고 단순하게 값을 반환하는 것이 좋음 (명시적으로 std::move를 사용하면 오히려 RVO를 방해할 수 있음)
  • 복잡한 조건부 반환이 있는 경우에도 컴파일러가 move constructor를 활용하여 최적화를 수행함

<참고 자료>

코드없는 프로그래밍
C++ Reference와 Move Semantics

0개의 댓글