Notice
Recent Posts
Recent Comments
Link
오븐 노트
[C++] 스마트 포인터 (smart pointer) 본문
#include <iostream>
using namespace std;
#include <vector>
#include <list>
#include <deque>
#include <map>
#include <set>
#include <algorithm>
class Knight
{
public:
Knight() { cout << "Knight 생성" << endl; }
~Knight() { cout << "Knight 소멸" << endl; }
void Attack()
{
if (_target.expired() == false) // weak_ptr로 인해 사용되는 expired. 해당 포인터가 살아있는가. false이면 유효
{
shared_ptr<Knight> sptr = _target.lock();
sptr-> _hp -= _damage;
cout << "HP: " << sptr->_hp << endl;
}
}
public:
int _hp = 100;
int _damage = 10;
weak_ptr<Knight> _target; // weak_ptr는 Knight의 소멸에 관여하지않기에 순환 구조가 일어날 수 없게 되지만, 번거롭다는 단점이 있다
//Knight* _target = nullptr; // 현대적인 C++에서는 이런식으로 포인터를 직접적으로 사용하지 않는다.
};
class RefCountBlock
{
public:
int _refCount = 1; // 해당 객체를 참조하는 것이 몇개인지
int _weakCount = 1;
};
template<typename T>
class SharedPtr
{
public:
SharedPtr() { }
SharedPtr(T* ptr) : _ptr(ptr)
{
if (_ptr != nullptr)
{
_block = new RefCountBlock();
cout << "RefCount : " << _block->_refCount << endl;
}
}
SharedPtr(const SharedPtr& sptr) : _ptr(sptr._ptr), _block(sptr._block)
{
if (_ptr != nullptr)
{
_block->_refCount++;
cout << "RefCount : " << _block->_refCount << endl;
}
}
void operator=(const SharedPtr& sptr)
{
_ptr = sptr._ptr;
_block = sptr._block;
if (_ptr != nullptr)
{
_block->_refCount++;
cout << "RefCount : " << _block->_refCount << endl;
}
}
~SharedPtr()
{
if (_ptr != nullptr)
{
_block->_refCount--; // _refCount를 감소 시키고 당장 delete하지 않는것이 포인트
cout << "RefCount : " << _block->_refCount << endl;
if (_block->_refCount == 0) // 한번 검증 한 후 아무도 사용하지 않는다는것을 확인하고 삭제
{
delete _ptr;
delete _block;
cout << "Delete Data" << endl;
}
}
}
public:
T* _ptr = nullptr;
RefCountBlock* _block = nullptr;
};
int main()
{
// -------생 포인터를 사용할 때-------
/*Knight* k1 = new Knight();
Knight* k2 = new Knight();
k1->_target = k2;
delete k2; // k2가 바로 삭제되어 k1이 가리키는 값이 엉뚱해져 버린다!
k1->Attack();*/
// 스마트 포인터 : 포인터를 알맞는 정책에 따라 관리하는 객체 (포인터를 래핑해서 사용)
// shared_ptr(대표), weak_ptr(shared의 단점을 메꾸기 위해 활용), unique_ptr(아예 별도)
// -------shared_ptr 구현부-------
//SharedPtr<Knight> k2;
//{
// SharedPtr<Knight> k1(new Knight());
// k2 = k1; // k1과 k2 모두 같은 객체를 참조하고 있는 상태이기에 삭제한다고 바로 삭제되지 않는다(shared_ptr)
//}
// -------shared_ptr 활용-------
//shared_ptr<Knight> k1 = make_shared<Knight>(); // new Knight()를 넣어줘도 일단은 되기는 하지만, 해당 버전이 성능이 좀 더 좋다 -> 메모리 블럭을 한 번에 만들어 줌
//{
// shared_ptr<Knight> k2 = make_shared<Knight>();
// k1->_target = k2; // 이미 날아간 메모리를 참조하는 dangling 문제 또는 use after free와 같은 문제에 대해서 일단 어느정도 자유로워진다는 장점
//} // 서버쪽에서는 특히나 멀티쓰레드가 되면 복잡해지기에 shared_ptr이나 자체 제작한 스마트 포인트를 활용한다.
// // 모던 C++부터는 생 포인터를 어지간하면 사용하지 않는다
//k1->Attack();
// -------weak_ptr를 사용하는 이유-------
shared_ptr<Knight> k1 = make_shared<Knight>();
// k1 [ refCount 2]
// k2 [ refCount 1] // 서로 주시하고 있기에 어떠한 상황이든 절대로 메모리가 소멸되지 않는 이슈가 발생
shared_ptr<Knight> k2 = make_shared<Knight>();
k1->_target = k2;
k2->_target = k1;
k1->Attack();
// -------shared_ptr만으로 만들 때 문제점-------
//k1->_target = nullptr; // 소멸할때쯤 밀어줘야한다
//k2->_target = nullptr; // ex) 유저와 몹이 서로 인식하여 메모리가 순환되어버림
unique_ptr<Knight> uptr = make_unique<Knight>(); // 말 그대로 세상에 혼자만 가지고 있어야하는 포인터
unique_ptr<Knight> uptr2 = std::move(uptr); // 때문에 이동론을 이용하여 소유권 자체를 넘기지 않는 한 넘길 수 없다
return 0;
}
[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 - 인프런 | 강의
C++ 카테고리의 글은 인프런 Rookiss님의 강의를 공부하며 정리하는 내용입니다.
이미 알고 있는 내용도 다시 정리 되어있을 수 있습니다.
모든 글은 제가 공부하기 위해 작성합니다.
'Develop > C++' 카테고리의 다른 글
[C++] 람다 (lambda) (0) | 2024.01.02 |
---|---|
[C++] 전달 참조 (forwarding reference) (0) | 2024.01.02 |
[C++] 오른값 참조 (rvalue reference) (0) | 2023.12.30 |
[C++] override, final (0) | 2023.12.28 |
[C++] delete(삭제된 함수) (0) | 2023.12.13 |