스마트 포인터(Smart Pointers) (C++11)
2025. 3. 28. 10:04ㆍProgramming Languages/C++
9.3 스마트 포인터(Smart Pointers) (C++11)
스마트 포인터는 동적으로 할당된 메모리 관리를 자동화하는 클래스 템플릿입니다.
9.3.1 unique_ptr
unique_ptr은 해당 자원의 유일한 소유자를 나타냅니다:
#include <iostream>
#include <memory>
#include <string>
class Resource {
private:
std::string name;
public:
Resource(const std::string& n) : name(n) {
std::cout << "Resource " << name << " 생성됨" << std::endl;
}
~Resource() {
std::cout << "Resource " << name << " 소멸됨" << std::endl;
}
void use() const {
std::cout << "Resource " << name << " 사용 중" << std::endl;
}
};
// unique_ptr을 함수의 반환 값으로 사용
std::unique_ptr<Resource> createResource(const std::string& name) {
return std::make_unique<Resource>(name); // C++14
// C++11에서는: return std::unique_ptr<Resource>(new Resource(name));
}
// unique_ptr을 함수 매개변수로 전달 (이동)
void consumeResource(std::unique_ptr<Resource> ptr) {
ptr->use();
// 함수가 종료되면 Resource가 자동으로 소멸됨
}
int main() {
// unique_ptr 생성
std::unique_ptr<Resource> res1 = std::make_unique<Resource>("First");
// 멤버 접근
res1->use();
// 소유권 이전
std::unique_ptr<Resource> res2 = std::move(res1);
// res1은 이제 nullptr
if (!res1) {
std::cout << "res1은 이제 비어 있습니다." << std::endl;
}
// res2 사용
res2->use();
// 함수 반환 값으로 unique_ptr 받기
auto res3 = createResource("Third");
res3->use();
// 함수에 unique_ptr 전달 (소유권 이전)
consumeResource(std::move(res3));
// res3은 이제 nullptr
if (!res3) {
std::cout << "res3은 이제 비어 있습니다." << std::endl;
}
// 배열 관리
std::unique_ptr<int[]> numbers = std::make_unique<int[]>(5);
for (int i = 0; i < 5; i++) {
numbers[i] = i * 10;
}
std::cout << "숫자: ";
for (int i = 0; i < 5; i++) {
std::cout << numbers[i] << " ";
}
std::cout << std::endl;
// 블록이 끝나면 모든 자원이 자동으로 소멸됨
return 0;
}
9.3.2 shared_ptr
shared_ptr은 참조 카운팅을 통해 여러 소유자가 리소스를 공유할 수 있게 합니다:
#include <iostream>
#include <memory>
#include <vector>
#include <string>
class Resource {
private:
std::string name;
public:
Resource(const std::string& n) : name(n) {
std::cout << "Resource " << name << " 생성됨" << std::endl;
}
~Resource() {
std::cout << "Resource " << name << " 소멸됨" << std::endl;
}
void use() const {
std::cout << "Resource " << name << " 사용 중" << std::endl;
}
std::string getName() const {
return name;
}
};
// shared_ptr 컨테이너 반환 함수
std::vector<std::shared_ptr<Resource>> createResources() {
std::vector<std::shared_ptr<Resource>> resources;
resources.push_back(std::make_shared<Resource>("Vector Resource 1"));
resources.push_back(std::make_shared<Resource>("Vector Resource 2"));
resources.push_back(std::make_shared<Resource>("Vector Resource 3"));
return resources; // 벡터가 리소스의 소유권을 유지함
}
// 기존 shared_ptr 사용 함수
void useResource(std::shared_ptr<Resource> res) {
std::cout << "참조 카운트: " << res.use_count() << std::endl;
res->use();
}
int main() {
// shared_ptr 기본 사용
auto res1 = std::make_shared<Resource>("Shared");
{
auto res2 = res1; // 참조 카운트 증가
std::cout << "블록 내부 - 참조 카운트: " << res1.use_count() << std::endl;
// 두 포인터 모두 같은 객체를 가리킴
res1->use();
res2->use();
} // 블록이 끝나면 res2가 소멸되고 참조 카운트 감소
std::cout << "블록 이후 - 참조 카운트: " << res1.use_count() << std::endl;
// 함수에 shared_ptr 전달 (참조 카운트 증가)
useResource(res1);
// shared_ptr 컨테이너
auto resources = createResources();
std::cout << "컨테이너 내 리소스:" << std::endl;
for (const auto& res : resources) {
std::cout << "- " << res->getName()
<< " (참조 카운트: " << res.use_count() << ")" << std::endl;
}
// 컨테이너에서 요소 추출
auto extracted = resources[1];
resources.erase(resources.begin() + 1);
std::cout << "추출 후 참조 카운트: " << extracted.use_count() << std::endl;
// shared_ptr 재설정
res1.reset(); // res1이 가리키던 자원 해제 (참조 카운트가 0이 되면 소멸)
std::cout << "res1 리셋 후: " << (res1 ? "not null" : "null") << std::endl;
// shared_ptr로 배열 관리 (C++17 이전에는 커스텀 삭제자 필요)
auto numbers = std::shared_ptr<int[]>(new int[5], std::default_delete<int[]>());
// C++17 이후: auto numbers = std::make_shared<int[]>(5);
for (int i = 0; i < 5; i++) {
numbers[i] = i * 100;
}
std::cout << "숫자: ";
for (int i = 0; i < 5; i++) {
std::cout << numbers[i] << " ";
}
std::cout << std::endl;
// 모든 블록이 끝나면 참조 카운트가 0이 되어 모든 자원이 소멸됨
return 0;
}
9.3.3 weak_ptr
weak_ptr은 shared_ptr이 관리하는 객체에 대한 약한 참조를 제공합니다. 순환 참조 문제를 해결하는 데 유용합니다:
#include <iostream>
#include <memory>
#include <string>
// 상호 참조 문제를 보여주는 클래스
class Person {
private:
std::string name;
// shared_ptr 대신 weak_ptr 사용
std::weak_ptr<Person> friend_; // 순환 참조 방지
public:
Person(const std::string& n) : name(n) {
std::cout << "Person " << name << " 생성됨" << std::endl;
}
~Person() {
std::cout << "Person " << name << " 소멸됨" << std::endl;
}
void setFriend(const std::shared_ptr<Person>& f) {
friend_ = f; // weak_ptr는 참조 카운트를 증가시키지 않음
}
void showFriend() const {
// weak_ptr을 사용하려면 lock()으로 shared_ptr 얻기
if (auto sharedFriend = friend_.lock()) {
std::cout << name << "의 친구: " << sharedFriend->name << std::endl;
} else {
std::cout << name << "의 친구는 없습니다." << std::endl;
}
}
std::string getName() const {
return name;
}
};
int main() {
// weak_ptr 기본 사용
std::weak_ptr<int> weakPtr;
{
auto sharedPtr = std::make_shared<int>(42);
weakPtr = sharedPtr; // shared_ptr을 weak_ptr에 할당
std::cout << "shared_ptr이 유효할 때:" << std::endl;
std::cout << "weakPtr.expired(): " << std::boolalpha << weakPtr.expired() << std::endl;
if (auto shared = weakPtr.lock()) {
std::cout << "값: " << *shared << std::endl;
} else {
std::cout << "weak_ptr이 유효하지 않습니다." << std::endl;
}
} // 블록이 끝나면 sharedPtr이 소멸되어 참조 카운트가 0이 됨
std::cout << "\nshared_ptr이 소멸된 후:" << std::endl;
std::cout << "weakPtr.expired(): " << weakPtr.expired() << std::endl;
if (auto shared = weakPtr.lock()) {
std::cout << "값: " << *shared << std::endl;
} else {
std::cout << "weak_ptr이 유효하지 않습니다." << std::endl;
}
// 순환 참조 문제 해결
std::cout << "\n순환 참조 예제:" << std::endl;
auto alice = std::make_shared<Person>("Alice");
auto bob = std::make_shared<Person>("Bob");
std::cout << "초기 참조 카운트 - Alice: " << alice.use_count()
<< ", Bob: " << bob.use_count() << std::endl;
// 친구 관계 설정
alice->setFriend(bob);
bob->setFriend(alice);
std::cout << "친구 설정 후 참조 카운트 - Alice: " << alice.use_count()
<< ", Bob: " << bob.use_count() << std::endl;
alice->showFriend();
bob->showFriend();
// alice와 bob이 서로를 weak_ptr로 참조하고 있으므로
// 여기서 shared_ptr이 소멸되면 객체도 소멸됨
std::cout << "\n스마트 포인터가 소멸됩니다..." << std::endl;
return 0;
}
'Programming Languages > C++' 카테고리의 다른 글
람다 표현식과 함수형 프로그래밍 (0) | 2025.03.28 |
---|---|
다중 스레딩과 동시성(C++11) (0) | 2025.03.28 |
파일 입출력 (0) | 2025.03.28 |
고급 주제 (0) | 2025.03.28 |
챕터8. 실습 문제 (0) | 2025.03.28 |