Modern C++-正の値を参照


#include <iostream>

#include <vector>

#include <list>

#include <deque>

#include <map>

#include <set>

#include<algorithm>

using namespace std;

// Modern C++ (C++11 부터 나온 아이들)

// 오른값(rvalue) 참조와 std::move

// - Modern C++의 꽃이라고 할 수 있다.

class Pet {

};

class Knight {

public:

 Knight() {

 cout << "Knight()" << endl;

 }

 Knight(const Knight& knight) {

 cout << "const Knight&" << endl;

 }

 ~Knight() {

 if (_pet)

 delete _pet;

 }

 // 복사 대입 연산자

 // 참고할 정보만 사용하고 인자는 변경 불가능

 void operator=(const Knight& knight) {

 cout << "operator=(const Knight&)" << endl;

 _hp = knight._hp;

 //_pet = knight._pet; // 펫을 공유한다? 말이 안되는데..

 if (knight._pet)

 _pet = new Pet(*knight._pet); // 깊은 복사가 필요

 }

 // 이동 대입 연산자

 // 인자를 수정하거나 꺼내쓰거나 맘대로 하쇼. 안쓸거니께

 void operator=(Knight&& knight) noexcept{

 cout << "operator=(const Knight&&)" << endl;

 // 얕은 복사

 _hp = knight._hp;

 _pet = knight._pet;

 knight._pet = nullptr;

 }

 void PrintInfo() {

 }

 void CPrintInfo() const {

 }

public:

 int _hp = 100;

 Pet* _pet = nullptr;

};

// 간단하긴 하지만 모든걸 복사해야하는 비효율과 원본을 넘겨주지 못하는 불편함이 있음

void TestKnight_Copy(Knight knight) {

 knight._hp = 200;

}

// 불필요한 복사가 일어나지 않고 원본에 접근이 가능한 장점이 있음

// 사용설명서: Knight를 넘겨 줄테니 수정하든 꺼내쓰든 알아서해!

void TestKnight_LValueRef(Knight& knight) {

 knight._hp = 200;

}

// const 참조값을 인자로 받기 때문에 rvalue 또한 인자가 될 수 있다.

// 하지만 인자로 받는 값은 수정이 불가능함

// 사용설명서: Knight를 넘겨 줄테니 수정하지말고 수정할 수 있는 모든 짓거리 불가!

void TestKnight_ConstLValueRef(const Knight& knight) {

 // 또한 이 함수의 매개변수인 const Knight& knight는 변경될 수 없는 인자기 때문에

 // 함수 내부에서 사용할 수 있는 knight의 멤버함수 또한 제한됨(const 선언이 되어있는 함수만 호출 가능)

 // knight.PrintInfo(); // 불가!

 knight.CPrintInfo(); // 가능!

}

// *오늘의 주제*

// 오른 값 참조를 받는 함수!

// 사용설명서: 읽고 쓰고 맘대로해 어차피 원본은 더이상 안쓸테니까

// -> 이동 대상, 원본 필요없어!

void TestKnight_RValueRef(Knight&& knight){

 knight._hp = 200; // 수정도 가능

}

// 원본을 유지하지 않아도 되는 것의 장점은 뭘까?

// 

int main()

{

 // 왼값(lvalue) vs 오른값(rvalue)

 // - lvalue : 단일식을 넘어서 계속 지속되는 개체

 // - rvalue : lvalue가 아닌 나머지 (임시 값, 열거형, 람다, i++ 등)

 int a = 3; // a는 지속되지만 3은 임시적으로 유효하다.

 //3 = a; // 사용 불가

 //a++ = 3; // 사용 불가

 Knight k1;

 TestKnight_Copy(k1);

 TestKnight_Copy(Knight());

 TestKnight_LValueRef(k1);

 //TestKnight_LValueRef(Knight()); // 비const 참조값을 인자로 받는 함수는 rvalue를 인자로 넘겨줄 수 없다.

 TestKnight_ConstLValueRef(Knight());// 함수 내부에서 변경하지 않는다는 조건이 걸려있기 때문에 rvalue를 넘겨줄 수 있다.

 

 

 //TestKnight_RValueRef(k1); // 왼값 참조를 못받음

 TestKnight_RValueRef(Knight()); // 오른값만 받을 수 있다.

 TestKnight_RValueRef(static_cast<Knight&&>(k1)); // 강제로 k1을 rvalue로 변환하여 넘겨줌.

 Knight k2;

 k2._pet = new Pet();

 k2._hp = 1000;

 Knight k3;

 // 'k2는 사용하고 버릴거야'라는 힌트를 줌, 이동 대입 연산자가 호출됨

 k3 = static_cast<Knight&&>(k2); 

 k3 = std::move(k2); // 오른값 참조로 캐스팅

 // std::move의 본래 이름 후보 중 하나가 rvalue_cast

 // -> 이 친구는 사용하고 버릴 이동 대상이다.

 // 속도차이가 꽤 난다

 std::unique_ptr<Knight> uptr = std::make_unique<Knight>(); // 이 포인터는 딱 하나만 존재해야해

 //std::unique_ptr<Knight> uptr2 = uptr; // 이 포인터의 복사를 막아둠

 std::unique_ptr<Knight> uptr2 = std::move(uptr); // 이 포인터를 이제 니가 담당해. 나는 버려

 return 0;

}