C++에서 unique_ptr 선언 및 사용

Shikha Chaudhary 2024년2월16일
  1. C++에서 unique_ptr 선언 및 사용
  2. C++의 고유 포인터
  3. 결론
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_ptrunique_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의 경우가 아닙니다. 이 예를 보십시오.

여기에는 p1p2라는 두 개의 고유한 포인터가 있으며 첫 번째 포인터를 두 번째 포인터에 복사하거나 할당합니다.

#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() 함수를 사용하여 고유 포인터의 소유권을 계속 이전할 수 있는 방법을 배웠습니다. 고유 포인터의 기능을 알고 있으므로 필요할 때 고유 포인터를 사용할 수 있기를 바랍니다.

관련 문장 - C++ Pointer