Menu
Coddy logo textTech

이동 생성자

Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 104개 중 21번째.

이동 생성자(move constructor)는 리소스를 복사하는 대신 임시 객체로부터 리소스를 이전합니다. 복사 생성자가 데이터를 복제하는 반면, 이동 생성자는 리소스를 "훔쳐" 오며, 소스 객체를 유효하지만 비어 있는 상태로 남겨둡니다. 이는 소스 객체가 어차피 파괴될 예정일 때 비용이 많이 드는 깊은 복사를 방지합니다.

이동 생성자는 &&로 표시되는 rvalue 참조를 인자로 받습니다:

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    // 이동 생성자
    Buffer(Buffer&& other) noexcept {
        data = other.data;       // 포인터 탈취
        size = other.size;
        other.data = nullptr;    // 소스를 유효한 상태로 남김
        other.size = 0;
    }
    
    ~Buffer() { delete[] data; }
};

복사와의 주요 차이점은 새로운 메모리를 할당하지 않는다는 것입니다. 우리는 단순히 기존 메모리의 소유권을 가져오고, 소스의 포인터를 nullptr로 설정하여 소멸자가 우리의 데이터를 삭제하지 않도록 합니다.

이동 생성자는 임시 객체로부터 초기화하거나 std::move()를 사용할 때 호출됩니다:

Buffer createBuffer() {
    return Buffer(1000);    // 이동 생성자 사용 (반환 값)
}

Buffer b1(1000);
Buffer b2(std::move(b1));   // 명시적 이동 - b1은 이제 비어 있음

가능한 경우 이동 생성자를 noexcept로 표시하세요. 이는 컴파일러에게 해당 연산이 예외를 던지지 않을 것임을 알려주며, std::vector와 같은 컨테이너에서 중요한 최적화를 가능하게 합니다.

challenge icon

챌린지

쉬움

이동 생성자가 리소스의 소유권을 어떻게 효율적으로 이전하는지 보여주는 데이터 패킷 시스템을 구축해 보겠습니다. 동적으로 할당된 정수 배열을 관리하는 DataPacket 클래스를 만들고, 데이터를 복사하는 대신 "훔쳐오는" 이동 생성자를 구현합니다.

코드를 정리하기 위해 두 개의 파일을 생성합니다:

  • DataPacket.h: 정수 데이터 페이로드를 관리하는 DataPacket 클래스를 정의합니다. 클래스에는 다음이 포함되어야 합니다:
    • Private 멤버: 정수 배열에 대한 포인터(payload), 요소의 개수를 나타내는 size, 패킷을 식별하기 위한 packetId (string)
    • 패킷 ID와 크기를 인자로 받는 매개변수화된 생성자: 배열을 할당하고 0부터 size-1까지의 값으로 채웁니다. "Packet <id> created with size <size>"를 출력합니다.
    • noexcept로 표시된 이동 생성자: 소스 객체로부터 페이로드의 소유권을 이전합니다. "Packet <id> moved"를 출력합니다. 소스 객체는 유효한 빈 상태(nullptr, size 0)로 남겨두어야 함을 기억하세요.
    • 소멸자: 포인터가 null이 아닌 경우 메모리를 해제하고, "Packet <id> destroyed"를 출력합니다.
    • 현재 크기를 반환하는 getSize() 메서드
    • 패킷 ID를 반환하는 getId() 메서드
    • 페이로드의 모든 요소의 합을 반환하는 getSum() 메서드 (페이로드가 null인 경우 0을 반환)
  • main.cpp: 입력으로부터 패킷 ID와 크기를 읽어 이동 생성을 시연합니다. 그 후:
    • 입력값을 사용하여 original이라는 이름의 DataPacket을 생성합니다.
    • "Original - ID: <id>, Size: <size>, Sum: <sum>"를 출력합니다.
    • std::move()를 사용하여 original로부터 이동하여 transferred라는 새 패킷을 생성합니다.
    • "After move:"를 출력합니다.
    • "Original - ID: <id>, Size: <size>, Sum: <sum>"를 출력합니다.
    • "Transferred - ID: <id>, Size: <size>, Sum: <sum>"를 출력합니다.

입력 형식은 다음과 같습니다:

  • 첫 번째 줄: 패킷 ID (string)
  • 두 번째 줄: 크기 (integer)

이동 후, 원본 패킷은 데이터가 이전되었으므로 크기가 0이고 합계가 0이어야 하며, 이전된 패킷은 모든 원본 데이터를 가지고 있어야 합니다. 이는 이동 시맨틱이 단순히 포인터 소유권을 이전함으로써 비용이 많이 드는 깊은 복사(deep copy)를 어떻게 피하는지 보여줍니다.

main.cpp에서 #include "DataPacket.h"를 사용하여 헤더 파일을 포함하고, std::move()를 위해 <utility>를 포함하는 것을 잊지 마세요.

치트 시트

이동 생성자(move constructor)는 임시 객체로부터 리소스를 복사하는 대신 전송하여, 비용이 많이 드는 깊은 복사(deep copy)를 피합니다. 이는 우측값 참조(rvalue reference)(&&)를 인자로 받아 리소스를 "훔쳐오고", 원본 객체는 유효하지만 비어 있는 상태로 남겨둡니다.

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    // Move constructor
    Buffer(Buffer&& other) noexcept {
        data = other.data;       // Steal the pointer
        size = other.size;
        other.data = nullptr;    // Leave source in valid state
        other.size = 0;
    }
    
    ~Buffer() { delete[] data; }
};

이동 생성자는 임시 객체로부터 초기화할 때나 std::move()를 사용할 때 호출됩니다:

Buffer createBuffer() {
    return Buffer(1000);    // Move constructor used (return value)
}

Buffer b1(1000);
Buffer b2(std::move(b1));   // Explicit move - b1 is now empty

표준 컨테이너에서 최적화를 활성화할 수 있도록 가능한 경우 이동 생성자를 noexcept로 표시하십시오.

직접 해보기

#include <iostream>
#include <string>
#include <utility>
#include "DataPacket.h"

using namespace std;

int main() {
    // 입력 읽기
    string packetId;
    int size;
    cin >> packetId;
    cin >> size;

    // TODO: 입력값을 사용하여 'original'이라는 이름의 DataPacket 생성

    // TODO: "Original - ID: <id>, Size: <size>, Sum: <sum>" 출력

    // TODO: original에서 이동(move)하여 'transferred'라는 이름의 새 패킷 생성
    // Hint: std::move()를 사용하세요

    // TODO: "After move:" 출력

    // TODO: "Original - ID: <id>, Size: <size>, Sum: <sum>" 출력

    // TODO: "Transferred - ID: <id>, Size: <size>, Sum: <sum>" 출력

    return 0;
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨