Fundamental of CS/: : C++

[Smart Pointer] Unique Pointer (std::unique_ptr)

Jay.P Morgan 2024. 7. 16. 11:07

 

 

Unique Pointer (std::unique_ptr) 정리

1. Unique Pointer란?

std::unique_ptr는 익스클루시브 오너십(Exclusive Ownership) 을 제공하는 스마트 포인터 입니다.

  • 하나의 객체는 단 하나의 unique_ptr만 소유할 수 있습니다.
  • 일반 포인터처럼 여러 개의 포인터가 같은 객체를 가리키는 것을 허용하지 않습니다.
  • 이를 통해 객체의 소유권을 명확하게 관리하고 메모리 관련 버그를 줄일 수 있습니다.

일반 포인터의 문제:

Cat* cat = new Cat();
Cat* cat2 = cat;

위 코드에서는 여러 포인터가 하나의 객체를 가리킬 수 있습니다.

cat  ──┐
       ├──> Cat Object
cat2 ──┘

이 구조에서는 누가 객체를 삭제해야 하는지 불명확함.

예를 들어 한쪽에서:

delete cat;

를 수행하면 다른 포인터는 이미 삭제된 객체를 가리키는 댕글링 포인터가 됩니다.

 

 

 

2. 일반 포인터의 문제점

여러 포인터가 하나의 객체를 공유 가능

예:

Cat* cat = new Cat();
Cat* cat1 = cat;

void func(Cat* ptr)
{
    Cat* ptr2 = ptr;
}

메모리 구조:

Stack

cat
 |
cat1
 |
ptr
 |
ptr2
 |
 v

Heap

Cat Object

하나의 객체를 여러 포인터가 가리킨다.

장점:

  • 객체를 복사하지 않아 효율적

문제:

  • 소유자가 불명확함
  • 잘못된 delete 가능
  • 메모리 릭 발생 가능

예:

void func(Cat* ptr)
{
    delete ptr;
}

이렇게 하면:

  • 객체는 이미 삭제됨
  • 다른 포인터들은 삭제된 메모리를 가리킴
  • 이중 삭제(double delete) 문제 발생 가능

 

 

 

3. unique_ptr 사용 방법

헤더:

#include <memory>

생성:

std::unique_ptr<Cat> catPtr = std::make_unique<Cat>();

unique_ptr은 내부적으로 객체의 생명주기를 관리한다.

스코프가 끝나면 자동으로:

  1. 객체 소멸자 호출
  2. 메모리 해제

가 수행된다.

따라서 직접:

delete

를 호출할 필요가 없다.

 

 

 

4. unique_ptr의 복사 금지

unique_ptr의 가장 중요한 특징:

객체의 소유권 복사를 허용하지 않는다.

예:

std::unique_ptr<Cat> cat1;

std::unique_ptr<Cat> cat2 = cat1;

컴파일 에러 발생.

이유:

cat1 ──┐
       ├──> Cat Object
cat2 ──┘

같은 객체를 두 개의 unique_ptr이 소유하는 상황을 막기 때문이다.

 

 

 

5. unique_ptr의 이동(move)

복사는 불가능하지만 소유권 이동(move) 은 가능하다.

std::unique_ptr<Cat> cat1 =
    std::make_unique<Cat>();

std::unique_ptr<Cat> cat2 =
    std::move(cat1);

결과:

이전:

cat1 ──> Cat Object
cat2


이후:

cat1
(null)

cat2 ──> Cat Object

소유권이 cat1에서 cat2로 이동한다.

즉:

  • 기존 소유자는 더 이상 객체를 관리하지 않음
  • 새로운 소유자가 객체를 관리

 

 

 

6. unique_ptr을 사용하는 대표적인 상황

클래스 멤버 변수로 포인터를 가지고 있어야 하는 경우

특히 다이나믹 폴리모피즘(Dynamic Polymorphism) 에서 많이 사용된다.

예:

class Animal
{
public:
    virtual void speak();
};

class Cat : public Animal
{
};

class Dog : public Animal
{
};

동물원 클래스:

class Zoo
{
    Animal* animal;
};

일반 포인터를 사용하면 문제가 발생한다.

필요한 작업:

  • destructor 작성
  • copy constructor 작성
  • move constructor 작성
  • assignment operator 작성

즉 Rule of 3/5를 직접 관리해야 한다.

 

 

 

 

7. unique_ptr을 이용한 개선

class Zoo
{
    std::unique_ptr<Animal> animal;
};

이렇게 변경하면:

  • 직접 delete 필요 없음
  • destructor 작성 필요 없음
  • 복사 문제 방지
  • 객체 소유권 명확

객체 구조:

Zoo

m_animal(unique_ptr)
       |
       v

Cat Object

Zoo 객체가 사라지면:

  1. unique_ptr 소멸
  2. Cat 객체 자동 삭제

 

 

 

8. unique_ptr과 상속

unique_ptr은 다형성과 함께 사용할 수 있다.

예:

std::unique_ptr<Animal> animal;

animal = std::make_unique<Cat>();

가능한 이유:

  • Cat은 Animal을 상속
  • Animal 포인터가 Cat 객체를 가리킬 수 있음
  • virtual destructor 필요
class Animal
{
public:
    virtual ~Animal(){}
};

 

 

 

 

핵심 정리

특  징 내    용
소유 방식 Exclusive Ownership
소유자 수 1개
복사 불가능
이동 가능 (std::move)
메모리 해제 자동
주요 목적 메모리 릭 방지
대표 사용처 클래스 멤버 포인터, 다형성 객체 관리

 

 

결론

std::unique_ptr는 객체의 소유권을 하나로 제한하여 안전한 메모리 관리를 가능하게 하는 스마트 포인터이다.

일반 포인터처럼 여러 곳에서 객체를 가리키는 구조를 막고, 객체의 생명주기를 소유자의 생명주기와 연결한다.

특히:

  • 클래스 내부에서 동적으로 생성되는 객체 관리
  • 상속 기반의 다형성 구현
  • 명확한 객체 소유권 관리

상황에서 가장 기본적으로 고려하는 스마트 포인터이다.

다음 단계에서는 여러 개의 스마트 포인터가 하나의 객체를 공유할 수 있는 std::shared_ptr와 참조 카운팅, 그리고 순환 참조로 인한 메모리 릭 문제를 학습한다.

 

 

 

'Fundamental of CS > : : C++' 카테고리의 다른 글

[Smart Pointer] Weak Pointer  (0) 2024.07.16
[Smart Pointer] Shared Pointer  (0) 2024.07.16
[Smart Pointer] Smart Pointer  (0) 2024.07.16
[Smart Pointer] C++ SmartPtr  (0) 2024.07.16
[Inheritance] C++ IO 상속 구조 정리  (0) 2024.07.16