이 글은 개인의 학습을 목적으로 정리한 글입니다. 이점 참고하고 읽어주세요 ;)
2019/12/02 - [프로그래밍/C++] - [C++] std::unique_ptr(1)
[C++] std::unique_ptr(1)
이 글은 개인의 학습을 목적으로 정리한 글입니다. 이점 참고하고 읽어주세요 ;) 2019/11/15 - [프로그래밍/C++] - [C++] std::move(1) [C++] std::move(1) 개인의 학습을 목적으로 정리한 글입니다. 이점 참고하..
movahws.tistory.com
이 글은 이전 포스팅과 연결됩니다.
void doSomething2(std::unique_ptr<Resource> &res)
{
res->setAll(10);
}
int main()
{
auto res1 = std::make_unique<Resource>(5);
res1->setAll(1);
res1->print(); // 1 1 1 1 1 출력
doSomething2(res1);
res1->print(); // 10 10 10 10 10 출력
}
이번에는 doSomething2 함수를 새로 정의했습니다. doSomething2 함수는 Resource 클래스의 unique_ptr 인스턴스인 res의 레퍼런스를 매개변수로 받습니다. 즉, copy semantics가 이뤄지지 않습니다.
위의 코드에서는 문제없이 res1이 doSomething2 함수의 인자로 전달되고, 함수 내에서 setAll 함수를 통해 배열을 10으로 다시 채우게 됩니다. 그리고 main 함수에서 res1의 멤버인 print를 호출하면 10 5개가 출력됩니다.
void doSomething2(std::unique_ptr<Resource> res) // copy semantics 발생!
{
res->setAll(10);
}
이번에는 doSomething2 함수에서 매개변수를 레퍼런스가 아닌 copy semantics가 발생하는 L-value로 받게 됩니다.
그렇다면 main함수에서는 어떤 변화가 생길까요?
int main()
{
auto res1 = std::make_unique<Resource>(5);
res1->setAll(1);
res1->print(); // 1 1 1 1 1 출력
// doSomething2(res1); -> res1은 unique_ptr로 copy semantics를 사용할 수 없기 때문에 error 발생
doSomething2(std::move(res1)); // res1이 가지고 있던 주소를 doSomething2 함수의 전달인자로 완전히 넘겨줌
cout << static_cast<bool>(res1) << endl; // res1은 주소를 넘겨주어 nullptr이기 때문에 0 출력
}
이전 코드와 같이 doSomething2 함수에 그대로 res1을 전달 인자로 주면 에러가 발생하게 됩니다. res1은 unique_ptr로 copy semantics를 허용하지 않기 때문입니다. 그렇다면 doSomething2 함수에 res1을 전달하고 싶으면 어떻게 해야 할까요?
방법은 doSomething2 함수에 std::move를 통해 res1이 가지고 있는 주소 값에 대한 소유권을 완전히 넘겨주면 됩니다.
그러면 doSomething2 함수는 std::move를 통해 완전히 넘겨받은 res1의 주소 값을 사용하게 되고,
동시에 res1은 unique_ptr이기 때문에 자신이 속한 코드 블록이 끝나는 때, 즉 doSomething2 함수가 종료될 때 소멸자를 호출합니다.
즉, 최종적으로 위와 같은 출력을 보이게 됩니다.
그런데 만약, std::move를 사용해서 doSomething함수에서 수정한 res1을 다시 main함수의 res1에 대입하고 싶을 땐 어떻게 하면 될까요?
std::unique_ptr<Resource> doSomething2(std::unique_ptr<Resource> res)
{
res->setAll(100);
return res;
}
int main()
{
auto res1 = std::make_unique<Resource>(5);
res1->setAll(1);
res1->print();
res1 = doSomething2(std::move(res1));
cout << std::boolalpha;
cout << static_cast<bool>(res1) << endl;
res1->print();
}
그럴 땐 위의 코드 블록과 같이,
doSomething2 함수의 리턴 타입을 Resource 클래스의 unique_ptr로 설정하고, 함수 안에서 수정한 res를 리턴으로 보내주면 됩니다. 그리고 main함수에서 nullptr이 된 res1에 doSomething 함수에서 리턴된 값을 대입해줍니다. 이 과정 역시 move semantics로 리턴된 값이 res1에 저장됩니다.
{
Resource* res = new Resource;
std::unique_ptr<Resource> res1(res);
std::unique_ptr<Resource> res2(res); // error!
delete res; // 동일 주소를 두 번 delete하기 때문에 문제 발생
}
마지막으로 주의하실 점을 간략하게 소개하고 포스팅을 마무리하겠습니다.
위의 코드 블록을 보면 Resource 클래스 자료형의 포인터로 동적 할당해준 res을 unique_ptr 인스턴스인 res1과 res2에 동시에 생성자에 대입하고 있습니다.
위에서 계속 설명했듯이 unique_ptr은 하나의 주소 값에 대해서는 오직 하나의 포인터 변수만 소유하고 관리할 수 있으므로, res1에 준 res를 다른 인스턴스에 복사해서 줄 수 없습니다.
또 한 가지는 맨 아랫줄의 delete 선언입니다. unique_ptr은 스스로 할당받은 주소을 관리하기 때문에 해당 스코프를 벗어나면 알아서 delete를 통해 포인터에 저장된 값을 지웁니다. 그런데 같은 스코프 안에서 delete를 또 선언하게 되면, 같은 주소에 대해서 delete가 두 번 일어나기 때문에 문제가 발생할 수 있습니다.
'Information Technology > C++' 카테고리의 다른 글
[C++] 순환 의존성 문제와 weak_ptr (0) | 2019.12.05 |
---|---|
[C++] std::shared_ptr (0) | 2019.12.04 |
[C++] std::unique_ptr(1) (0) | 2019.12.02 |
[C++] STL의 반복자 iterator (0) | 2019.11.30 |
[C++] 표준 템플릿 라이브러리, 컨테이너 소개 (0) | 2019.11.30 |