본문 바로가기
-----ETC-----/C++ 성능 최적화 및 고급 테크닉 시리즈

[C++ 성능 최적화 및 고급 테크닉] Day 8: Move semantics와 R-값 참조

by cogito21_cpp 2024. 8. 1.
반응형

Move Semantics와 R-값 참조의 중요성

C++11에서 도입된 Move Semantics(이동 시멘틱스)와 R-값 참조는 자원의 이동을 통해 성능을 최적화할 수 있는 강력한 도구입니다. 특히, 객체의 복사가 빈번하게 발생하는 상황에서 큰 성능 향상을 가져올 수 있습니다.

 

R-값 참조

R-값 참조는 임시 객체와 같이 수명이 짧은 값을 가리키는 참조입니다. R-값 참조를 통해 객체를 복사하지 않고 이동할 수 있습니다. 이를 통해 불필요한 복사를 방지하고, 자원 관리 효율을 높일 수 있습니다.

int&& rvalue = 10;

 

Move Semantics

이동 시멘틱스는 복사 대신 자원을 이동하는 기법입니다. 이동 생성자와 이동 할당 연산자를 사용하여 구현할 수 있습니다.

 

이동 생성자

#include <iostream>

class MyClass {
public:
    int* data;

    MyClass() : data(new int[1000]) {
        std::cout << "Constructor" << std::endl;
    }

    ~MyClass() {
        delete[] data;
        std::cout << "Destructor" << std::endl;
    }

    // 이동 생성자
    MyClass(MyClass&& other) noexcept : data(other.data) {
        other.data = nullptr;
        std::cout << "Move Constructor" << std::endl;
    }

    // 이동 할당 연산자
    MyClass& operator=(MyClass&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            other.data = nullptr;
            std::cout << "Move Assignment" << std::endl;
        }
        return *this;
    }

    // 복사 생성자와 복사 할당 연산자 삭제
    MyClass(const MyClass&) = delete;
    MyClass& operator=(const MyClass&) = delete;
};

MyClass createObject() {
    MyClass obj;
    return obj; // 이동 생성자가 호출됩니다.
}

int main() {
    MyClass obj1 = createObject(); // 이동 생성자가 호출됩니다.
    MyClass obj2;
    obj2 = std::move(obj1); // 이동 할당 연산자가 호출됩니다.

    return 0;
}

 

std::move와 std::forward

std::move

std::move는 객체를 R-값 참조로 캐스팅하여 이동 시멘틱스를 사용할 수 있게 합니다.

MyClass obj1;
MyClass obj2 = std::move(obj1); // 이동 생성자 호출

 

std::forward

std::forward는 완벽한 전달(perfect forwarding)을 구현하는 데 사용됩니다. 이는 템플릿 함수에서 인자를 원래의 L-값 또는 R-값으로 전달할 때 유용합니다.

#include <iostream>
#include <utility>

void process(int& lval) {
    std::cout << "L-value reference" << std::endl;
}

void process(int&& rval) {
    std::cout << "R-value reference" << std::endl;
}

template<typename T>
void forwarder(T&& arg) {
    process(std::forward<T>(arg));
}

int main() {
    int x = 10;
    forwarder(x); // L-value 전달
    forwarder(10); // R-value 전달
    return 0;
}

 

실습 문제

문제 1: 이동 시멘틱스를 사용하여 성능 최적화

다음 코드에서 이동 시멘틱스를 사용하여 성능을 최적화하세요.

 

main.cpp

#include <iostream>
#include <vector>

class MyClass {
public:
    int* data;

    MyClass() : data(new int[1000]) {
        std::cout << "Constructor" << std::endl;
    }

    ~MyClass() {
        delete[] data;
        std::cout << "Destructor" << std::endl;
    }

    // 복사 생성자
    MyClass(const MyClass& other) : data(new int[1000]) {
        std::copy(other.data, other.data + 1000, data);
        std::cout << "Copy Constructor" << std::endl;
    }

    // 복사 할당 연산자
    MyClass& operator=(const MyClass& other) {
        if (this != &other) {
            std::copy(other.data, other.data + 1000, data);
            std::cout << "Copy Assignment" << std::endl;
        }
        return *this;
    }
};

MyClass createObject() {
    MyClass obj;
    return obj;
}

int main() {
    MyClass obj1 = createObject();
    MyClass obj2 = obj1;

    return 0;
}

 

해답:

main.cpp (최적화)

#include <iostream>
#include <vector>

class MyClass {
public:
    int* data;

    MyClass() : data(new int[1000]) {
        std::cout << "Constructor" << std::endl;
    }

    ~MyClass() {
        delete[] data;
        std::cout << "Destructor" << std::endl;
    }

    // 이동 생성자
    MyClass(MyClass&& other) noexcept : data(other.data) {
        other.data = nullptr;
        std::cout << "Move Constructor" << std::endl;
    }

    // 이동 할당 연산자
    MyClass& operator=(MyClass&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            other.data = nullptr;
            std::cout << "Move Assignment" << std::endl;
        }
        return *this;
    }

    // 복사 생성자와 복사 할당 연산자 삭제
    MyClass(const MyClass&) = delete;
    MyClass& operator=(const MyClass&) = delete;
};

MyClass createObject() {
    MyClass obj;
    return obj;
}

int main() {
    MyClass obj1 = createObject(); // 이동 생성자가 호출됩니다.
    MyClass obj2 = std::move(obj1); // 이동 생성자가 호출됩니다.

    return 0;
}

 

이제 여덟 번째 날의 학습을 마쳤습니다. Move semantics와 R-값 참조의 중요성을 이해하고, 실습 문제를 통해 이를 적용하는 방법을 학습했습니다.

질문이나 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "완벽한 전달 (perfect forwarding)"에 대해 학습하겠습니다.

반응형