2018년 5월 21일 월요일

C++ 11/14 관련 내용 대충 정리, unique_ptr

최신 C++을 공부하려 책을 보긴 했었는데
내용이 기존 문법과 함께 여기저기 흩어져 있어 보기에 좀 어려워
MSDN을 참고하여 대충 정리하려함.

C++11/14/17 기능에 대한 지원(최신 C++)
https://msdn.microsoft.com/ko-kr/library/hh567368.aspx


unique_ptr

https://msdn.microsoft.com/ko-kr/library/hh279676.aspx

- 일반 C++ 개체에 대한 스마트 포인터가 필요할 경우 사용
- unique_ptr은 포인터를 공유 하지 않고 이동만 가능함.
 : 이동 시 메모리 리소스의 소유권이 이전되어 원래 unique_ptr는 더 이상 소유하지 않음.
- 다른 unique_ptr로 복사, 함수에 값으로 전달하거나 사본이 필요한 STL(표준 템플릿 라이브러리) 알고리즘에서는 사용 불가
- unique_ptr을 생성할 경우 되도록이면 make_unique 도우미 함수를 사용
 : c++ 14 지원을 위해 compile option(-std=c++14
- <memory> 헤더의 std namespace에 정의 되어 있음.





unique_ptr<Song> SongFactory(const std::wstring& artist, const std::wstring& title)
{
    // Implicit move operation into the variable that stores the result.
    return make_unique<Song>(artist, title);
}

void MakeSongs()
{
    // Create a new unique_ptr with a new object.
    auto song = make_unique<Song>(L"Mr. Children", L"Namonaki Uta");

    // Use the unique_ptr.
    vector<wstring> titles = { song->title };

    // Move raw pointer from one unique_ptr to another.
    unique_ptr<Song> song2 = std::move(song);

    // Obtain unique_ptr from function that returns by value.
    auto song3 = SongFactory(L"Michael Jackson", L"Beat It");
}

일반적인 사용 예제, song 변수가 가졌던 개체는 song2로 이전(move)되어 song의 member들은 nullptr이 됨.

void SongVector()
{
    vector<unique_ptr<Song>> songs;

    // Create a few new unique_ptr<Song> instances
    // and add them to vector using implicit move semantics.
    songs.push_back(make_unique<Song>(L"B'z", L"Juice"));
    songs.push_back(make_unique<Song>(L"Namie Amuro", L"Funky Town"));
    songs.push_back(make_unique<Song>(L"Kome Kome Club", L"Kimi ga Iru Dake de"));
    songs.push_back(make_unique<Song>(L"Ayumi Hamasaki", L"Poker Face"));

    // Pass by const reference when possible to avoid copying.
    for (const auto& song : songs)
    {
        wcout << L"Artist: " << song->artist << L"   Title: " << song->title << endl; 
    }    
}

이동이 불가하므로 vector 로 바로 생성된 개체를 전달함.
vector내 개체를 접근하기 위해 const auto& 로 reference를 참조함.
(const auto song : songs) 은 unique_ptr을 복사하려는 것이므로 컴파일 에러 발생


// Create a unique_ptr to an array of 5 integers.
 auto p = make_unique<int[]>(5);

 // Initialize the array.
 for (int i = 0; i < 5; ++i)
 {
  p[i] = i;
  wcout << p[i] << endl;
 }

배열에 대한 unique_ptr를 make_unique를 사용해서 생성할 수 있지만 초기화는 별도로..


Modern Effective C++에서 관련된 chapter는 다음과 같고
더 자세한 내용을 원한다면 책을 꼭 읽는 것을 추천.
단 Modern Effective C++이 처음이라면 Effective C++을 먼저 읽는 것을 추천.

Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14
: http://a.co/0MIIC9k

Item 18:Use std::unique_ptr for exclusive-ownership resource management.
Item 21:Prefer std::make_unique and std::make_shared to direct use of new.


참고할 만한 것들을 보면

아래와 같이 Investment를 상속하는 여러 class들이 있을 경우

class Investment { … };
class Stock:  public Investment { … };
class Bond:  public Investment { … };
class RealEstate:  public Investment { … };

template<typename... Ts>                // return std::unique_ptr std::unique_ptr<Investment>             // to an object created 
makeInvestment(Ts&&... params);       // from the given args

이렇게 factory 함수인 makeInvestment를 만들어 Investment를 가지는 unique_ptr를 제공할 수 있음.

{
   ...
   auto pInvestment =                // pInvestment is of type     
   makeInvestment( arguments );  // std::unique_ptr<Investment>
   ...
}

이렇게 사용을 하게 될 것이고 생성된 pInvestment는 변수의 scope을 벗어나게 되면 자동으로 소멸 되게 되는게 일반적인 사용 시나리오 일 것임.

함께 unique_ptr는 기본적으로 개체의 소멸자를 호출하겠지만 custom deleter를 직접 지정할 수 있음.

auto delInvmt = [](Investment* pInvestment)       // custom
                      {                                          // deleter
                         makeLogEntry(pInvestment);   // (a lambda
                         delete pInvestment;              // expression)
                      };
template<typename... Ts>                                // revised
std::unique_ptr<Investment, decltype(delInvmt)>   // return type
makeInvestment(Ts&&... params)


그리고 c++14의 auto를 사용하게 된다면.. 좀 더 간편할 것임.

template<typename... Ts>
auto makeInvestment(Ts&&... params)              // C++14

다만 일반적인 unique_ptr의 사이즈는 raw pointer와 동일하겠지만 custom deleter를 가진 unique_ptr의 사이즈는 한두 word의사이즈가 추가된다. 함께 custom deleter로 function object를 사용하는것 보다 stateless function object인 lambda를 사용하는 것이 unique_ptr의 사이즈를 늘리지 않는다고 함.

그리고 unique_ptr를 생성할 때는 new 대신 std::make_unique를 사용하는 것을 추천하고 있음.
다만 std::make_unique는 c++14에서 지원하고 있어 compile option 설정 필요.


댓글 없음:

댓글 쓰기