방법: shared_ptr 인스턴스 만들기 및 사용
shared_ptr
형식은 둘 이상의 소유자가 개체의 수명을 관리해야 하는 시나리오를 위해 디자인된 C++ 표준 라이브러리의 스마트 포인터입니다. shared_ptr
을 초기화한 후 복사, 함수 인수의 값으로 전달 및 다른 shared_ptr
인스턴스로 할당할 수 있습니다. 모든 인스턴스는 동일한 개체를 가리키고 새 shared_ptr
이 추가되거나 범위를 벗어나거나 다시 설정될 때마다 하나의 "제어 블록"에 대한 액세스를 공유합니다. 참조 횟수가 0에 도달하면 메모리 리소스 및 제어 블록이 삭제됩니다.
다음 그림에서는 한 개의 메모리 위치를 가리키는 여러 shared_ptr
인스턴스를 보여 줍니다.
첫 번째 다이어그램은 참조 수가 1인 제어 블록뿐만 아니라 MyClass 인스턴스를 가리키는 공유 포인터 P1을 보여 줍니다. 두 번째 다이어그램은 MyClass 인스턴스와 이제 참조 수가 2인 공유 제어 블록을 가리키는 또 다른 공유 포인터 P2가 추가된 모습을 보여 줍니다.
예제 설정
다음에 나오는 예제에서는 다음과 같이 필수 헤더를 포함하고 필요한 형식을 선언했다고 가정합니다.
// shared_ptr-examples.cpp
// The following examples assume these declarations:
#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
struct MediaAsset
{
virtual ~MediaAsset() = default; // make it polymorphic
};
struct Song : public MediaAsset
{
std::wstring artist;
std::wstring title;
Song(const std::wstring& artist_, const std::wstring& title_) :
artist{ artist_ }, title{ title_ } {}
};
struct Photo : public MediaAsset
{
std::wstring date;
std::wstring location;
std::wstring subject;
Photo(
const std::wstring& date_,
const std::wstring& location_,
const std::wstring& subject_) :
date{ date_ }, location{ location_ }, subject{ subject_ } {}
};
using namespace std;
int main()
{
// The examples go here, in order:
// Example 1
// Example 2
// Example 3
// Example 4
// Example 6
}
예 1
가능하면 메모리 리소스를 처음으로 만들 때 make_shared 함수를 사용하여 shared_ptr
을 만듭니다. make_shared
는 예외로부터 안전합니다. 동일한 호출을 사용하여 제어 블록 및 리소스에 대한 메모리를 할당하므로 생성 오버헤드가 감소됩니다. make_shared
를 사용하지 않는 경우 shared_ptr
생성자에 전달하기 전에 명시적 new
식을 사용하여 개체를 만들어야 합니다. 다음 예제에서는 새 개체와 함께 shared_ptr
을 선언하고 초기화하는 다양한 방법을 보여 줍니다.
// Use make_shared function when possible.
auto sp1 = make_shared<Song>(L"The Beatles", L"Im Happy Just to Dance With You");
// Ok, but slightly less efficient.
// Note: Using new expression as constructor argument
// creates no named variable for other code to access.
shared_ptr<Song> sp2(new Song(L"Lady Gaga", L"Just Dance"));
// When initialization must be separate from declaration, e.g. class members,
// initialize with nullptr to make your programming intent explicit.
shared_ptr<Song> sp5(nullptr);
//Equivalent to: shared_ptr<Song> sp5;
//...
sp5 = make_shared<Song>(L"Elton John", L"I'm Still Standing");
예제 2
다음 예제에서는 다른 shared_ptr
로 할당된 개체의 소유권을 공유하는 shared_ptr
인스턴스를 선언하고 초기화하는 방법을 보여 줍니다. sp2
가 초기화된 shared_ptr
임을 가정하십시오.
//Initialize with copy constructor. Increments ref count.
auto sp3(sp2);
//Initialize via assignment. Increments ref count.
auto sp4 = sp2;
//Initialize with nullptr. sp7 is empty.
shared_ptr<Song> sp7(nullptr);
// Initialize with another shared_ptr. sp1 and sp2
// swap pointers as well as ref counts.
sp1.swap(sp2);
예 3
shared_ptr
은 요소를 복사하는 알고리즘을 사용할 때 C++ 표준 템플릿 라이브러리 컨테이너에서도 유용합니다. shared_ptr
에 요소를 래핑한 다음 내부 메모리가 필요할 때까지 유효하거나 더 이상 유효하지 않음을 인식하며 해당 요소를 다른 컨테이너에 복사할 수 있습니다. 다음 예제에서는 벡터의 remove_copy_if
인스턴스에서 shared_ptr
알고리즘을 사용하는 방법을 보여 줍니다.
vector<shared_ptr<Song>> v {
make_shared<Song>(L"Bob Dylan", L"The Times They Are A Changing"),
make_shared<Song>(L"Aretha Franklin", L"Bridge Over Troubled Water"),
make_shared<Song>(L"Thalía", L"Entre El Mar y Una Estrella")
};
vector<shared_ptr<Song>> v2;
remove_copy_if(v.begin(), v.end(), back_inserter(v2), [] (shared_ptr<Song> s)
{
return s->artist.compare(L"Bob Dylan") == 0;
});
for (const auto& s : v2)
{
wcout << s->artist << L":" << s->title << endl;
}
예시 4
dynamic_pointer_cast
, static_pointer_cast
및 const_pointer_cast
를 사용하여 shared_ptr
을 캐스팅할 수 있습니다. 이러한 함수는 dynamic_cast
, static_cast
및 const_cast
연산자와 비슷합니다. 다음 예제에서는 기본 클래스에서 shared_ptr
의 벡터에 있는 각 요소의 파생 형식을 테스트한 다음 요소를 복사하고 이에 대한 정보를 표시하는 방법을 보여 줍니다.
vector<shared_ptr<MediaAsset>> assets {
make_shared<Song>(L"Himesh Reshammiya", L"Tera Surroor"),
make_shared<Song>(L"Penaz Masani", L"Tu Dil De De"),
make_shared<Photo>(L"2011-04-06", L"Redmond, WA", L"Soccer field at Microsoft.")
};
vector<shared_ptr<MediaAsset>> photos;
copy_if(assets.begin(), assets.end(), back_inserter(photos), [] (shared_ptr<MediaAsset> p) -> bool
{
// Use dynamic_pointer_cast to test whether
// element is a shared_ptr<Photo>.
shared_ptr<Photo> temp = dynamic_pointer_cast<Photo>(p);
return temp.get() != nullptr;
});
for (const auto& p : photos)
{
// We know that the photos vector contains only
// shared_ptr<Photo> objects, so use static_cast.
wcout << "Photo location: " << (static_pointer_cast<Photo>(p))->location << endl;
}
예제 5
shared_ptr
을 다음과 같은 방법으로 다른 함수에 전달할 수 있습니다.
shared_ptr
을 값으로 전달합니다. 이 방법은 복사 생성자를 호출하고 참조 횟수를 늘리며 호출 수신자를 소유자로 변경합니다. 이 작업에는 적은 양의 오버헤드가 있으며 이는 전달하는 상당 수의shared_ptr
개체 수에 따라 달라질 수 있습니다. 호출자와 호출 수신자 간의 암시적 또는 명시적 코드 계약이 호출 수신자가 소유자일 것을 요구하는 경우 이 옵션을 사용합니다.참조 또는 const 참조로
shared_ptr
을 전달합니다. 이 경우 참조 횟수는 늘어나지 않고 호출 수신자는 호출자가 범위를 벗어나지 않는 한 포인터에 액세스할 수 있습니다. 또는 호출 수신자는 참조를 기반으로shared_ptr
을 만들고 공유 소유자가 될 수 있습니다. 호출자가 호출 수신자를 모르는 경우 또는shared_ptr
을 전달해야 하고 성능상의 이유로 복사 작업을 하지 않아야 할 경우 이 옵션을 사용합니다.내부 포인터 또는 내부 개체에 대한 참조를 전달합니다. 이를 통해 호출 수신자는 개체를 사용하지만 소유권을 공유하거나 수명을 연장할 수 없습니다. 호출 수신자가 원시 포인터에서
shared_ptr
을 만드는 경우 새shared_ptr
은 원본에 독립적이고 내부 리소스를 제어하지 않습니다. 호출자와 호출 수신자 사이의 계약이 호출자가shared_ptr
수명에 대한 소유권을 보유함을 명확하게 지정하는 경우 이 옵션을 사용하십시오.shared_ptr
을 전달하는 방법을 결정할 때 호출 수신자가 내부 리소스의 소유권을 공유해야 하는지 여부를 확인합니다. "소유자"는 기본 리소스를 필요할 때까지 내부 리소스를 활성 상태로 유지할 수 있는 개체 또는 함수입니다. 호출 수신자가 포인터의 수명을 해당(함수의) 수명 이상으로 확장할 수 있음을 보장해야 하는 경우 첫 번째 옵션을 사용합니다. 호출 수신자의 수명 연장 여부를 고려하지 않는 경우 참조로 전달하고 호출 수신자는 이를 복사하거나 복사하지 않습니다.내부 포인터에 도우미 함수 액세스를 제공해야 하고 도우미 함수가 포인터를 사용하고 호출 함수가 반환되기 전에 반환하는 것을 알고 있는 경우 해당 함수는 내부 포인터의 소유권을 공유하지 않아야 합니다. 호출자의
shared_ptr
수명 내에 포인터에 액세스해야 합니다. 이러한 경우 참조로shared_ptr
을 전달하거나 원시 포인터 또는 참조를 내부 개체로 전달하는 것이 안전합니다. 이 방법으로 전달하면 약간의 성능 이점이 제공되고 프로그래밍 의도를 표시하는 데 유용할 수도 있습니다.경우에 따라
std::vector<shared_ptr<T>>
의 예제에서 각shared_ptr
을 람다 식 본문 또는 명명된 함수 개체로 전달해야 할 수도 있습니다. 람다 또는 함수가 포인터를 저장하지 않는 경우 참조로shared_ptr
을 전달하여 각 요소에 대한 복사 생성자를 호출하지 않습니다.
void use_shared_ptr_by_value(shared_ptr<int> sp);
void use_shared_ptr_by_reference(shared_ptr<int>& sp);
void use_shared_ptr_by_const_reference(const shared_ptr<int>& sp);
void use_raw_pointer(int* p);
void use_reference(int& r);
void test() {
auto sp = make_shared<int>(5);
// Pass the shared_ptr by value.
// This invokes the copy constructor, increments the reference count, and makes the callee an owner.
use_shared_ptr_by_value(sp);
// Pass the shared_ptr by reference or const reference.
// In this case, the reference count isn't incremented.
use_shared_ptr_by_reference(sp);
use_shared_ptr_by_const_reference(sp);
// Pass the underlying pointer or a reference to the underlying object.
use_raw_pointer(sp.get());
use_reference(*sp);
// Pass the shared_ptr by value.
// This invokes the move constructor, which doesn't increment the reference count
// but in fact transfers ownership to the callee.
use_shared_ptr_by_value(move(sp));
}
예제 6
다음 예제에서는 shared_ptr
이 shared_ptr
인스턴스가 소유하는 메모리의 포인터를 비교할 수 있도록 다양한 비교 연산자를 오버로드하는 방법을 보여 줍니다.
// Initialize two separate raw pointers.
// Note that they contain the same values.
auto song1 = new Song(L"Village People", L"YMCA");
auto song2 = new Song(L"Village People", L"YMCA");
// Create two unrelated shared_ptrs.
shared_ptr<Song> p1(song1);
shared_ptr<Song> p2(song2);
// Unrelated shared_ptrs are never equal.
wcout << "p1 < p2 = " << std::boolalpha << (p1 < p2) << endl;
wcout << "p1 == p2 = " << std::boolalpha <<(p1 == p2) << endl;
// Related shared_ptr instances are always equal.
shared_ptr<Song> p3(p2);
wcout << "p3 == p2 = " << std::boolalpha << (p3 == p2) << endl;
참고 항목
피드백
https://aka.ms/ContentUserFeedback
출시 예정: 2024년 내내 콘텐츠에 대한 피드백 메커니즘으로 GitHub 문제를 단계적으로 폐지하고 이를 새로운 피드백 시스템으로 바꿀 예정입니다. 자세한 내용은 다음을 참조하세요.다음에 대한 사용자 의견 제출 및 보기