반응형

기존 C++ 포인터는 동적 할당 메모리를 생성(new) 해서 사용한 후 반드시 삭제(delete) 해줘야 한다.

프로그래머가 수동으로 관리하다보니 삭제가 누락되면 메모리 릭이 발생하곤 한다.

C++11 에서 추가된 Smart Pointer를 사용하면 메모리 삭제에 대한 부분을 신경쓰지 않아도 된다.

Smart Pointer에는 3가지(unique_ptr, shared_ptr, weak_ptr)가 있으며, weak_ptr은 사용 빈도가 매우 낮으므로 unique_ptr shared_ptr 에 대해 알아보자.

 

1. 사용법

#include <memory>

std::unique_ptr< var-type > var;
std::shared_ptr< var-type > var;

 

(1) 인자

  1-1) var-type : 변수나 객체의 타입을 지정

 

2. 일반 포인터 사용

- 포인터를 생성하여 사용한 후 반드시 삭제를 해줘야 한다.

- 프로그래머의 실수로 삭제가 누락될 경우 메모리 릭이 발생할 우려가 있다.

#include <iostream>
#include <string>

using Type = std::string;

int main()
{
    Type* str{new Type("Hello world !!!")};
    std::cout << *str << std::endl;
    
    delete str; // 동적 메모리 사용 후 삭제 필요

    return 0;
}

 

3. std::unique_ptr

- 소유권을 하나만 가질 수 있는 포인터이다.

- 객체를 선언하여 사용한 후 사용 범위가 종료될 때 소멸자가 호출되어 메모리를 자동으로 삭제한다. 

- 소유권을 이전하기 위해서는 std::move()를 사용해야거나 참조로 전달해야 한다.

- C++14 이후에는 std::make_unique() 함수를 통해 포인터를 생성할 수 있다.

#include <iostream>
#include <string>
#include <memory>

using Type = std::string;

void print(std::unique_ptr<Type>& pStr4)
{
    std::unique_ptr<Type> pStr5{std::move(pStr4)};
    std::cout << pStr4 << std::endl; // 0
    std::cout << pStr5 << std::endl; // 0x141fc20
    
    // 함수가 종료되면 pStr5는 삭제됨.
}

int main()
{
    std::unique_ptr<Type> pStr{new Type("Hello world !!!")};
    //std::unique_ptr<Type> pStr{std::make_unique<Type>("Hello world !!!")}; // C++14 이후
    std::cout << pStr << std::endl; // 0x141fc20
    
    Type* ppStr{pStr.get()}; // 주소값 가져오기
    std::cout << ppStr << std::endl; // 0x141fc20
    
    //std::unique_ptr<Type> pStr2{pStr}; // 컴파일 에러!! (소유권은 하나만 가능)
    std::unique_ptr<Type> pStr3{std::move(pStr)}; // 소유권 이전 가능
    std::cout << pStr << std::endl; // 0
    std::cout << pStr3 << std::endl; // 0x141fc20
    
    print(pStr3); // 인자로 넘겨서 참조로 받기 가능
    std::cout << pStr3 << std::endl; // 0
    
    // 동적 메모리 사용 후 삭제가 필요하지 않음.

    return 0;
}

 

4. std::shared_ptr

- 소유권을 여러개 가질 수 있는 포인터이다.

- 소유자 수의 관리는 내부적으로 참조 카운터(Reference Counter)를 통해 이루어진다.

- 참조 카운터가 0이 되면 메모리를 자동으로 삭제한다.

- C++14 이후에는 std::make_shared() 함수를 통해 포인터를 생성할 수 있다.

#include <iostream>
#include <string>
#include <memory>

using Type = std::string;

void print(std::shared_ptr<Type>& pStr3)
{
    std::shared_ptr<Type> pStr4{std::move(pStr3)};
    std::cout << pStr4 << " - " << pStr4.use_count() << std::endl; // 0x2274c20 - 2
    pStr4.reset(); // 명시적으로 메모리 해제
    
    // 함수가 종료되면 참조 카운트가 1 감소됨.
}

int main()
{
    std::shared_ptr<Type> pStr{new Type("Hello world !!!")};
    //std::shared_ptr<Type> pStr{std::make_shared<Type>("Hello world !!!")}; // C++14 이후
    std::cout << pStr << " - " << pStr.use_count() << std::endl; // 0x2274c20 - 1

    Type* ppStr{pStr.get()}; // 주소값 가져오기
    std::cout << pStr << " - " << pStr.use_count() << std::endl; // 0x2274c20 - 1

    std::shared_ptr<Type> pStr2{pStr}; // 소유권 여러개 가능
    std::cout << pStr2 << " - " << pStr2.use_count() << std::endl; // 0x2274c20 - 2

    print(pStr2); // 인자로 넘겨서 참조로 받기 가능
    std::cout << pStr2 << " - " << pStr2.use_count() << std::endl; // 0 - 0

    print(pStr);
    std::cout << pStr << " - " << pStr.use_count() << std::endl; // 0 - 0
    
    // 동적 메모리 사용 후 삭제가 필요하지 않음.
    
    return 0;
}

 



 

반응형

+ Recent posts