람다(lambda)
람다 표현식. 정말 유용하게 사용할 수 있는 문법이다.
오른쪽 참조는 새로 추가된 문법으로 C++의 속도를 향상 시킬 수 있는 문법인것에 반면
람다는 굳이 사용하지 않더라도 지금까지 배운 내용으로도 대신할 수 있는 문법이다.
람다를 다르게 말하면 함수 객체를 빠르게 만드는 문법이다.
#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
enum class ItemType
{
None,
Armor,
Weapon,
Jewelry,
Consumable,
};
enum class Rarity
{
Common,
Rare,
Unique,
};
class Item
{
public:
Item(){}
Item(int itemId, Rarity rarity, ItemType type)
: _itemId(itemId), _rarity(rarity), _type(type)
{
}
public:
int _itemId = 0;
Rarity _rarity = Rarity::Common;
ItemType _type = ItemType::None;
};
int main()
{
vector<Item> v;
v.push_back(Item(1, Rarity::Common, ItemType::Weapon));
v.push_back(Item(2, Rarity::Common, ItemType::Armor));
v.push_back(Item(3, Rarity::Rare, ItemType::Jewelry));
v.push_back(Item(4, Rarity::Unique, ItemType::Weapon));
// 람다 = 함수 객체를 손 쉽게 만드는 문법
// 람다 자체로 C++11에 새로운 기능이 들어간 것은 아니다.
{
// 람다 표현식
// 람다에 의해 만들어진 실행시점 객체를 클로저라고 한다.
// 클로저를 만들어서 사용 가능
auto isUniqueLambda = [](Item& item)
{
// 구현부
return item._rarity == Rarity::Unique;
};
auto findIt = find_if(v.begin(), v.end(),isUniqueLambda);
// 한번에 넣어주기 가능 (재 사용 안함)
auto findIt = find_if(v.begin(), v.end(), [](Item& item)
{
// 구현부
return item._rarity == Rarity::Unique;
});
// [] 캡처 : 함수 객체 내부에서 변수를 저장하는 개념과 유사
// 사진을 찰칵 캡처 하듯. 일종의 스냅샷을 찍는다고 이해
// 기본 캡처 모드 : 값(복사) 방식(=), 참조 방식(&)
int itemId = 4;
auto findByItemLambda = [=](Item& item) { return item._itemId == itemId; };
auto findIt = find_if(v.begin(), v.end(), findByItemLambda);
// 변수마다 캡처 모드를 지정해서 사용이 가능하다.
}
return 0;
}
스마트 포인터(smart pointer)
포인터는 특정 메모리의 값에 접근하고 그 값을 수정할 수 있는 장점이 있고 그것이 단점이 될 수도 있다.
만약 A와 B 플레이어가 있는데 A가 B를 타겟으로 포인터를 통해 들고 있고 공격해본다고 생각해보자. A가 공격을 하기 전에 B가 접속을 종료해서 힙 메모리에서 내려갔다고 해보면 A의 공격을 무시가 될까?? 아니다 바로 이상한 메모리에 접근해 공격을 하는 현상이 발생해 매우 위험할 것이다. 그래서 요즘은 생 포인터를 사용하는 것이 아닌 스마트 포인터를 사용한다.
스마트 포인터는 포인터를 알맞는 정책에 따라 관리하는 객체로 포인터를 래핑해서 사용한다.
shared_ptr, weak_ptr, unique_ptr 이 3개가 대표적이다.
shared_ptr을 만들어 보면 다음과 같다.
#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
class RefCountBlock
{
public:
int _refCount = 1;
};
template<typename T>
class SharedPtr
{
public:
SharedPtr(){}
SharedPtr(T* ptr) :_ptr(ptr)
{
_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;
}
}
~SharedPtr()
{
if (_ptr != nullptr)
{
_block->_refCount--;
cout << "RefCount : " << _block->_refCount << endl;
if (_block->_refCount == 0)
{
delete _ptr;
delete _block;
cout << "Delete Data" << endl;
}
}
}
void operator=(const SharedPtr& sptr)
{
_ptr = sptr._ptr;
_block = sptr._block;
if (_ptr != nullptr)
{
_block->_refCount++;
cout << "RefCount : " << _block->_refCount << endl;
}
}
public:
T* _ptr;
RefCountBlock* _block;
};
class Knight
{
public:
};
int main()
{
SharedPtr<Knight> k2;
{
SharedPtr<Knight> k1(new Knight());
k2 = k1;
}
return 0;
}
실제로는 이렇게 구현되어 있지 않지만 비슷한 방식으로 되어있다.
실제 shared_ptr을 사용하는 방법이다.
class Knight
{
public:
shared_ptr<Knight> _target = nullptr;
};
int main()
{
shared_ptr<Knight> k1 = make_shared<Knight>();
{
shared_ptr<Knight> k2 = make_shared<Knight>();
k1->_target = k2;
}
// k1이 가리키는 값이 사라지지 않음(k2가 안사라짐)
return 0;
}
만약 k1과 k2가 서로 shared_ptr을 사용해 가리키고 있다면 어떠한 경우에도 절대 메모리에서 사라지지 않는다.
그러면 메모리 사용량이 쌓여서 크래시가 나게된다.
이렇게 순환 구조를 이룰때는 weak_ptr을 사용하면 된다.
weak_ptr은 refcount와 weakcount를 가지고 판단하게된다.
class Knight
{
public:
void Attack()
{
if (_target.expired() == false)
{
shared_ptr<Knight> sptr = _target.lock();
// 작업...
}
}
weak_ptr<Knight> _target;
};
int main()
{
shared_ptr<Knight> k1 = make_shared<Knight>();
{
shared_ptr<Knight> k2 = make_shared<Knight>();
k1->_target = k2;
}
// 이제는 사라짐
k1->Attack();
return 0;
}
k2가 사라지게 되면 k1이 가리키는 target이 메모리에서 사라졌다고 해서 if문이 통과되지 않고 그대로 끝난다.
unique_ptr은 세상에서 하나밖에 없는, 혼자만 가리키는, 복사가 막힌 포인터라고 생각하면 된다.
스마트 포인터는 포인터의 사용이 끝나면 자동으로 메모리를 해제시켜주니까 우리가 그동안 메모리를 직접 관리하면서 스트레스 받던걸 그나마 줄일 수 있다.
'C++ > 기초' 카테고리의 다른 글
[Modern C++] 오른값 참조(rvalue reference), 전달 참조(forwarding reference) (1) | 2023.12.06 |
---|---|
[Modern C++] using, enum class, delete(삭제된 함수), override, final (0) | 2023.12.06 |
[Modern C++] auto, 중괄호 초기화, nullptr (1) | 2023.12.05 |
[C++] STL - deque, map, set, multimap, multiset (1) | 2023.12.05 |
[C++] STL - List (0) | 2023.12.04 |