leesangwon0114

I am Research Engineer. Currently working in KT.

C++Intermediate 23. move semantics#2

19 Nov 2018 » c++

codenuri 강석민 강사 강의 내용기반으로 정리한 내용입니다.


using move

#include <iostream>

class Test
{
public:
    Test() {}
    ~Test() {}
    Test(const Test& t) { cout << "Copy" << endl; }
    Test(Test&& t) { cout << "Move" << endl; }

    Test& operator=(const Test& t) { 
        cout << "Copy=" << endl;
        return *this; 
    } // 복사 대입연산자
    Test& operator=(Test&& t) { 
        cout << "Move=" << endl;
        return *this; 
    } // move 대입연산자
};

template<typename T> void Swap(T& x, T& y)
{
    Test temp = x; // 복사 생성자
    x = y; // 복사 대입
    y = temp;
}

int main()
{
    Test t1, t2;
    Swap(t1, t2);
}

Copy Copy= Copy= 순으로 출력됨

Swap을 복사 없이 이동만으로 만들기

move 와 swap

#include <iostream>
#include <algorithm>

class Test
{
public:
    Test() {}
    ~Test() {}
    Test(const Test& t) { cout << "Copy" << endl; }
    Test(Test&& t) { cout << "Move" << endl; }

    Test& operator=(const Test& t) { 
        cout << "Copy=" << endl;
        return *this; 
    } // 복사 대입연산자
    Test& operator=(Test&& t) { 
        cout << "Move=" << endl;
        return *this; 
    } // move 대입연산자
};

template<typename T> void Swap(T& x, T& y)
{
    Test temp = move(x);
    x = move(y);
    y = move(temp);
}

int main()
{
    Test t1, t2;
    // Swap(t1, t2);
    swap(t1, t2);
}

Move Move= Move= 순으로 출력됨

이미 STL의 Swap은 move 로 구현되어 있음

만일 move 를 안만들었다면 복사생성자가 불림


move 와 버퍼 복사

#include <iostream>
#include <vector>
#include <algorithm>

class Test
{
public:
    Test() {}
    ~Test() {}
    Test(const Test& t) { cout << "Copy" << endl; }
    // Test(Test&& t) { cout << "Move" << endl; }
    Test(Test&& t) noexcept { cout << "Move" << endl; }

    Test& operator=(const Test& t) { 
        cout << "Copy=" << endl;
        return *this; 
    } // 복사 대입연산자
    // Test& operator=(Test&& t) { 
    Test& operator=(Test&& t) noexcept { 
        cout << "Move=" << endl;
        return *this; 
    } // move 대입연산자
};

template<typename T> void Swap(T& x, T& y)
{
    Test temp = move(x);
    x = move(y);
    y = move(temp);
}

int main()
{
    Test* p1 = new Test[2];
    Test* p2 = new Test[4];

    for(int i = 0; i < 2; i++)
        // p2[i] = p1[i]; // copy 대입
        p2[i] = move(p1[i]); // move 대입

    vector<Test> v(2);
    v.resize(4);
}

작은 버퍼에서 큰 버퍼로 옮길 때 move 생성자를 쓰는 것이 성능 효과적

STL의 vector 가 move 로 되어 있음

noexcept 를 붙여야 Move 이며 Move 대입이 아니라 Move 생성을 사용함


move & noexcept

STL이 어떻게 복사를 하고 있는지

#include <iostream>
#include <vector>
#include <algorithm>
#include <type_traits>

class Test
{
public:
    Test() { cout << "Test()" << endl; }
    ~Test() { cout << "~Test()" << endl; }
    Test(const Test& t) { cout << "Copy" << endl; }
    Test(Test&& t) noexcept { cout << "Move" << endl; }

    Test& operator=(const Test& t) { 
        cout << "Copy=" << endl;
        return *this; 
    }
    Test& operator=(Test&& t) noexcept { 
        cout << "Move=" << endl;
        return *this; 
    }
};

template<typename T> void Swap(T& x, T& y)
{
    Test temp = move(x);
    x = move(y);
    y = move(temp);
}

int main()
{
    /*
    Test* p1 = new Test[2];
    Test* p2 = new Test[4];

    for(int i = 0; i < 2; i++)
        p2[i] = move(p1[i]); // move 대입
    */

    Test t1;
    Test t2 = t1; // copy
    Test t3 = move(t2); // move

    bool b = is_nothrow_move_constructible<Test>::value; // move 실패 여부를 조사할 수 있는 traits(예외 있으면 true)

    cout << b << endl;

    Test t4 = move_if_noexcept(t1); // t1의 move 생성자의 예외가 없으면 move로 있으면 copy로 옮김
}

move 생성자의 예외가 없을 때만 move를 하는 것이 안전한 코드이고 이를 위해 noexcept 키워드를 적어주어야함


move를 활용한 버퍼 복사

#include <iostream>
#include <vector>
#include <algorithm>
#include <type_traits>
using namespace std;

class Test
{
public:
    Test() { cout << "Test()" << endl; }
    ~Test() { cout << "~Test()" << endl; }
    Test(const Test& t) { cout << "Copy" << endl; }
    Test(Test&& t) noexcept { cout << "Move" << endl; }

    Test& operator=(const Test& t) { 
        cout << "Copy=" << endl;
        return *this; 
    }
    Test& operator=(Test&& t) noexcept { 
        cout << "Move=" << endl;
        return *this; 
    }
};

int main()
{
    Test* p1 = new Test[2];
    // Test* p2 = new Test[4];
    // 1. 신규 버퍼는 메모리만 할당 한다.
    Test* p2 = static_cast<Test*>(operator new(sizeof(Test)*4));

    for(int i = 0; i < 2; i++)
    {
        // p2[i] = move(p1[i]);
        // new(&p2[i]) Test; // 디폴트 생성자 호출
        // new(&p2[i]) Test(p1[i]); // 복사 생성자
        // new(&p2[i]) Test(move(p1[i])); // move 생성자

        new(&p2[i]) Test(move_if_noexcept(p1[i]));
    }

    // 2. 새로운 객체는 디폴트 생성자 호출
    for(int i = 2; i < 4; i++)
    {
        new(&p2[i]) Test; // 디폴트 생성자 호출.
    }
}

#include <iostream>
#include <vector>
#include <algorithm>
#include <type_traits>
using namespace std;

class Test
{
public:
    Test() { cout << "Test()" << endl; }
    ~Test() { cout << "~Test()" << endl; }
    Test(const Test& t) { cout << "Copy" << endl; }
    Test(Test&& t) noexcept { cout << "Move" << endl; }

    Test& operator=(const Test& t) { 
        cout << "Copy=" << endl;
        return *this; 
    }
    Test& operator=(Test&& t) noexcept { 
        cout << "Move=" << endl;
        return *this; 
    }
};

int main()
{
    Test* p1 = static_cast<Test*>(operator new(sizeof(Test)*2));
    for(int i = 0; i < 2; i++)
    {
        new(&p1[i]) Test;
    }

    Test* p2 = static_cast<Test*>(operator new(sizeof(Test)*4));

    for(int i = 0; i < 2; i++)
    {
        new(&p2[i]) Test(move_if_noexcept(p1[i]));
    }
    for(int i = 2; i < 4; i++)
    {
        new(&p2[i]) Test;
    }

    // 최초 버퍼 파괴
    for(int i = 1; i >= 0; i--)
    {
        p1[i].~Test();
    }

    operator delete(p1);

    cout << "--" << endl;

    // 신규 버퍼 파괴
    for(int i = 3; i >= 0; i--)
    {
        p2[i].~Test();
    }

    operator delete(p2);
}
#include <iostream>
#include <vector>
using namespace std;

class Test
{
public:
    Test() { cout << "Test()" << endl; }
    ~Test() { cout << "~Test()" << endl; }
    Test(const Test& t) { cout << "Copy" << endl; }
    Test(Test&& t) noexcept { cout << "Move" << endl; }

    Test& operator=(const Test& t) { 
        cout << "Copy=" << endl;
        return *this; 
    }
    Test& operator=(Test&& t) noexcept { 
        cout << "Move=" << endl;
        return *this; 
    }
};

int main()
{
    vector<Test> v(2);
    v.resize(4);
    cout << "--" << endl;
}