C++에서 unique_ptr 선언 및 사용
포인터는 C++에서 많은 유용한 용도로 사용됩니다. 프로그램의 공간 절약에서 데이터 구조 구현 지원에 이르기까지 포인터는 C 및 C++와 같은 프로그래밍 언어에 매우 중요한 도구입니다.
이 기사에서는 그러한 포인터 중 하나인 고유 포인터의 개념에 대해 설명합니다. C++의 표준 라이브러리는 이 포인터를 스마트 포인터의 구현 중 하나로 제공합니다.
이 스마트 포인터가 지금 무엇인지 궁금하십니까? 읽어; 우리는 당신을 위해 모든 것을 다루었습니다.
C++에서 unique_ptr
선언 및 사용
우리는 포인터가 다른 변수의 주소를 저장하는 데 사용된다는 것을 이미 알고 있습니다. 이러한 포인터는 여러 곳에서 유용하지만 중요한 문제가 있습니다.
주어진 코드 블록을 살펴보십시오. 대부분의 경우 다음과 같이 포인터를 선언한 다음 사용하지만 삭제하지는 않습니다.
void demo() {
int *d = new int;
*d = 10;
}
이로 인해 메모리 누수 문제가 발생합니다. 사용 후 메모리 섹션이 할당 해제되거나 해제되지 않을 때 발생합니다.
따라서 다른 프로그램에서 사용할 수 없거나 여유 공간이 없는 메모리는 공간 낭비로 이어지고 성능을 저하시킵니다. 이것은 메모리 누수에 지나지 않습니다.
프로그램이 더 복잡해지면 전체 힙 메모리가 쓸모없게 될 수도 있습니다. 따라서 C++11은 이 문제를 해결하기 위해 스마트 포인터라는 개념을 제시했습니다.
C++의 스마트 포인터
Java 및 C#과 같은 언어에는 사용되지 않는 메모리가 자동으로 할당 해제되는 메커니즘이 있습니다. 따라서 프로그래머는 가비지 수집 시스템이 처리할 포인터를 할당 해제할 필요가 없습니다.
C++의 경우 스마트 포인터를 사용하여 이 작업을 수행합니다. 스마트 포인터는 원시 포인터와 *
및 ->
와 같은 오버로드 연산자를 래핑하는 단순한 클래스일 뿐입니다.
스마트 포인터 클래스의 개체는 일반 포인터와 동일하게 보이지만 일반 포인터와 달리 자체적으로 사용되지 않는 메모리 공간을 할당 해제할 수 있습니다. 네 가지 유형의 스마트 포인터가 C++ 표준 라이브러리의 <memory>
헤더 내부에 정의되어 있으며 그 중 unique_ptr
이 그 중 하나입니다.
이제 우리는 C++의 고유 포인터에 대해 배우기 시작할 준비가 되었습니다.
C++의 고유 포인터
C++의 auto_ptr
을 대체하기 위해 unique_ptr
이 개발되었습니다. 원시 포인터와 달리 unique_ptr
은 원시 포인터의 컨테이너이며 배타적 소유권과 메모리 누수가 없음을 보장합니다.
C++에서 unique_ptr
선언
아래 코드는 C++에서 고유 포인터를 선언하는 방법을 보여줍니다.
std::unique_ptr<Type> p(new Type);
여기에서 10
과 같이 정수를 가리키는 고유 포인터를 생성하려는 경우 위의 구문이 변경되는 방식입니다.
std::unique_ptr<int> p(new int(10));
위의 코드 줄은 값 10
을 가리키는 고유 포인터 p
를 의미합니다. 이 기본 선언이 어떻게 작동하는지 이해해야 합니다.
new
표현식은 위의 코드 행에서 포인터 p
를 생성합니다. 그러나 동일한 선언을 아래 주어진 것으로 변경하면 의미가 변경됩니다.
std::unique_ptr<int> p2(x);
이제 포인터는 변수 x
에 저장됩니다. 개념적으로는 동일합니다.
우리는 두 곳 모두에서 unique_ptr
을 만들고 있지만 두 번째 방법을 사용하는 것은 바람직하지 않습니다.
두 번째 방법을 사용하면 다음과 같이 할 수 있습니다.
std::unique_ptr<int> p2(x);
std::unique_ptr<int> p3(x);
//....
std::unique_ptr<int> p6(x);
이는 둘 이상의 고유 포인터가 동일한 개체를 소유한다는 것을 의미하며, 이는 다음 섹션에서 볼 수 있듯이 고유 포인터의 의미 체계에 위배됩니다.
또한 C++14에는 다음과 같이 make_unique
를 사용하여 unique_ptr
을 선언하는 옵션이 있습니다.
unique_ptr<int> d = make_unique<int>(10);
이렇게 하면 10
값을 가리키는 고유한 포인터 d
가 생성됩니다.
이제 unique_ptr
의 다양한 기능을 하나씩 살펴보겠습니다.
C++ unique_ptr
자동 삭제
원시 포인터를 사용하면 프로그래머는 종종 사용 후 포인터를 삭제하는 것을 잊어버려 메모리 누수 문제가 발생합니다. unique_ptr
은 unique_ptr
이 범위를 벗어날 때 보유하고 있는 개체를 파괴하므로 이러한 번거로움을 줄여줍니다.
개체는 unique_ptr
이 소멸되거나 다른 포인터가 unique_ptr
에 할당될 때 소멸됩니다. 이는 delete
연산자를 사용하여 개체를 파괴하고 메모리 할당을 해제하는 get_deleter() (ptr)
의 도움으로 수행됩니다.
따라서 unique_ptr
을 사용할 때 동적으로 할당된 개체를 삭제할 필요가 없으며 unique_ptr
의 소멸자가 이를 처리합니다.
다음은 포인터가 소멸되는 외부 범위를 보여주는 예시 코드입니다.
void demo() { unique_ptr<int> demo(new int); }
< -- -- -- -- -- -- -- -- -- -- -- -- -scope of the pointer ends here
컨트롤이 이러한 중괄호 밖으로 나가면 개체가 소멸됩니다.
C++ unique_ptr
의 독점 소유권
unique_ptr
의 다음이자 매우 중요한 기능은 개체에 대한 배타적 소유권을 제공하며 복사할 수 없습니다. 이는 하나의 unique_ptr
만이 동시에 하나의 객체만 가리킬 수 있음을 의미합니다.
이것이 unique_ptr
을 복사할 수 없는 이유이기도 합니다. 종종 원시 포인터를 사용하면 일반적인 할당으로 인해 포인터가 복사됩니다.
그러나 이것은 unique_ptr
의 경우가 아닙니다. 이 예를 보십시오.
여기에는 p1
과 p2
라는 두 개의 고유한 포인터가 있으며 첫 번째 포인터를 두 번째 포인터에 복사하거나 할당합니다.
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int> p1(new int);
unique_ptr<int> p2 = p1;
cout << "Success";
return 0;
}
출력:
use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]'
8 | unique_ptr<int> p2 = p1;
컴파일 타임 오류가 발생하고 인쇄 문이 실행되지 않는 것을 볼 수 있습니다. 이는 C++에서 동시에 하나의 객체만 가리키는 unique_ptr
을 복사할 수 없음을 분명히 보여줍니다.
이제 원시 포인터로 이 작업을 수행했다면 print
문이 성공적으로 실행되었을 것입니다. 이것은 아래에서 수행됩니다.
#include <iostream>
using namespace std;
int main()
{
int *ptr1;
int *ptr2;
ptr2 = ptr1;
cout << "Success";
return 0;
}
출력:
Success
이번에는 포인터가 원시 포인터이기 때문에 복사됩니다. 이것이 본질적으로 복제와 관련하여 unique_ptr
이 작동하는 방식입니다.
C++ unique_ptr
소유권 이전
앞에서 보았듯이 단순히 복사하는 것만으로는 unique_ptr
의 소유권을 변경할 수 없습니다. 여기서 해결책은 C++ 표준 라이브러리에서 제공하는 move()
함수를 사용하는 것입니다.
할당 연산자를 사용하여 unique_ptr
복사를 시도한 것과 동일한 코드를 사용하고 있습니다. 그러나 이번에는 이 할당 중에 move()
함수를 사용합니다.
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int> p1(new int);
unique_ptr<int> p2 = move(p1); // using the move() function
cout << "Success";
return 0;
}
출력:
Success
이제 move()
함수를 사용했기 때문에 오류가 발생하지 않고 print
문도 실행됩니다.
이것이 객체의 소유권을 다른 unique_ptr
로 이전할 수 있는 방법이지만 어떤 경우든 unique_ptr
이 가리키는 객체는 한 번에 하나씩 유지됩니다.
이것들은 원시 포인터와 다르게 만드는 unique_ptr
의 주요 기능이었습니다.
고유 포인터에 대한 자세한 내용은 이 문서를 참조하세요.
결론
이 기사에서는 C++의 고유 포인터 개념을 살펴보았습니다. 고유 포인터는 독점 소유권 및 자동 삭제 기능으로 알려진 스마트 포인터입니다.
선언 방법과 move()
함수를 사용하여 고유 포인터의 소유권을 계속 이전할 수 있는 방법을 배웠습니다. 고유 포인터의 기능을 알고 있으므로 필요할 때 고유 포인터를 사용할 수 있기를 바랍니다.