짧은 설명
- 배열 : 저장 및 순회해서 비교
- 포인터는 nullptr이 기본값이다
vector
vector<int> v1; // 빈 벡터
vector<int> v2(5); // 크기 5, 모두 0으로 초기화
vector<int> v3(5, 10); // 크기 5, 모두 10으로 초기화
vector<int> v4 = {1, 2, 3, 4, 5}; // 초기값 지정
- []: 인덱스 연산자도 사용할 수 있다.
- size()를 이용해서 순회도 가능하다.
- stl에는 순회용 또는 포인터를 위해 존재하는 클래스가 있다.
- iterator 클래스다.
- begin(), end() : 이 둘이 iterator 객체다.
vector 함수
- vector.push_back(데이터형과 같은 값) : 배열의 맨 뒤에 인자로 들어온 값을 할당하는 함수다.
- vector.size() : 배열의 사이즈를 반환하는 함수다.
- vector.pop_back() : 배열의 맨 뒤에 값을 삭제하는 함수다.
- vector.insert(삽입 위치, 요소) : 중간 삽입
- ex) v1.insert(v1.begin(), 100);
- v1.insert(v1.begin() + 2, 100); ⇒ 3번째 위치에 삽입
- vector.erase(삭제 위치) : 중간 삭제
- ex) v1.erase(v1.begin() + 2);
- vector.erase(시작 위치, 끝 위치) : 범위 삭제
- v1.erase(v1.begin(), v1.begin() + 2); ⇒ 끝 바로 전까지 삭제 / 즉, 1 ~ 3까지니까 1번과 2번만 삭제된다.
iterator
std::vector<int>::iterator
for (std::vector<int>::iterator it = v1.begin(); it != v1.end(); ++it)
{
std::cout << *it << std::endl;
}
auto it = v1.begin();
std::cout << *(it + 3) << std::endl; //it의 4번째 요소값이 반환된다.
- 데이터형은 T 인자를 공유한다.
- 반복변수라고 생각하면 된다.
- std::vector<int>::iterator를 auto로 대체할 수 있다.
- 포인터 연산자로 참조해야한다.
- begin은 std::vector<int>::iterator 데이터형을 반환한다.
- ++it : ++ 연산자가 재구현 되어있는 것이다.
- *it : * 연산자가 재구현 되어있는 것이다.
- 출력 결과는 v1의 첫번째 요소가 반환되는 것이다.
- 즉, 포인터처럼 주소를 가리켜 * 연산자로 값을 반환하는 용도로 사용한다.
- ++it는 다음 주소의 요소를 가리키게 된다.
- iterator는 vector에만 사용하는게 아니라 stl의 모든 클래스에도 사용된다.
- east?에도 똑같이 begin()을 사용해서 순회가 가능해진다.
- begin() : iterator 객체를 생성해서 반환한다.
- 역할 : 첫번쨰 요소를 찾아 iterator 객체로 만들어 반환한다.
- 주의점 : end()는 끝 요소를 리턴하는것이 아니다.
- 끝이 아닌 그 다음에 해당하는 위치의 iterator가 리턴되는 것이다.
- 끝 그 다음의 용도 : 빈 공간 그 이전까지 순회하라는 뜻이다.
- 조건식 < 을 생각하면 된다.
- push_back의 값이 들어올 위치가 이곳이다.
- 중간값 삽입 및 삭제도 iterator를 사용한다.
list
- LinkedList : iterator 관련 클래스다.
- 산술 연산이 불가능하다. ⇒ 랜덤 액세스가 불가능하다.
- 랜덤 액세스 : v1.begin() + 2 같이 주소에 +해서 다음 요소에 접근하는것이 안된다.
- 대신, push_front() 함수가 있어서 앞 뒤에 자유롭게 넣을 수 있게된다.
list 함수
- push_front(요소) : 배열의 앞에 삽입
- list.erase(v1.begin(), v1.end() ) : 전체 지우기
LinkedList 자료 구조
- 순차적 자료구조(컨테이너)다.
- 값 + 다음 노드(위치)의 주소 가 저장되어있다.
배열과 차이점
- 배열 : 찾을 위치를 알면 + 연산을 해서 해당 위치를 접근이 가능하다.
- 링크드 리스트 : 처음부터 N 번째까지 접근해야 찾을 위치에 접근이 가능하다.
- 특정 요소 삭제시 그 노드만 빼고 주소를 그 다음 주소로 연결한다. ⇒ 삽입 삭제가 배열보다 편하다.
- 빈번하게 삽입 삭제가 일어난다 ⇒ 리스트가 유리 / 저장과 순회가 빈번하게 일어난다 ⇒ 배열이 유리
SFML Framework 준비
- 불편했던 점
- 텍스쳐, 폰트, 사운드 등 반복적인 리소스 생성
- 리소스 로드 및 언로드
- 상호 작용 - 키보드 을 눌리는지 확인하고 재사용할 수 있게
- 입력처리 : 이벤트 루프
- 키보드 뿐만 아니라 마우스도
- 인풋 매니저, 리소스 매니저(로드, 언로드) 초기버전(템플릿으로) 생성
방법
- static 멤버를 써서 싱글턴 패턴의 전역 변수처럼 쓴다.
InputManager
- 키 눌림 및 해제 확인
- list 함수를 사용한다.
//키 입력 확인
static bool GetKeyDown(sf::Keyboard::Key key); //눌렸을떄
static bool GetKeyUp(sf::Keyboard::Key key); //땠을떄
static bool GetKey(sf::Keyboard::Key key); //키 입력 확인
- 키가 있는지 확인하고, 확인 후 삭제를 해야한다.
//키 있는지 확인 함수(키 리스트, 검사할 키)
static bool Contains(const std::list<sf::Keyboard::Key>& list, sf::Keyboard::Key key);
//키 있는지 확인 후 삭제 함수(키 리스트, 검사할 키)
static void Remove(std::list<sf::Keyboard::Key>& list, sf::Keyboard::Key key);
static void Init(); //초기화 함수
static void Clear(); //이벤트 루프 전 키 초기화 함수
- 매 프레임 업데이트와 입력 이벤트를 처리할 업데이트가 필요하다.
//sf::Event& : 크기가 크기에 참조로 원본 그대로 가져와야 최적화에 좋다.
static void UpdateEvent(const sf::Event& ev); //이벤트 루프 호출 함수
//dt : 델타 타임
static void Update(float dt); //매 wjscp 루프(업데이트)때 해야할 함수
싱글턴 디자인 패턴
- 특정 클래스의 객체가 클래스 처음부터 끝까지 하나만 유지되게끔 사용하는 패턴이다.
- 이 하나뿐인 객체를 어디서든 접근하는것이다.
- 하나만 유지되게 생성해야한다.
- 코드 어디서든 접근할 수 있게끔 해야한다.
public:
//싱글턴 인스턴스 반환
static T& Instance()
{
static T instance; //스테틱 지역 변수
return instance;
}
- 리소스 매니저에 사용한다.
- 생성자를 public으로 두면 안된다.
protected:
Singleton() = default; //= default == {} 즉, 아무 값도 없는것과 같은것이다.
virtual ~Singleton() = default; //virtual : 소멸자는 자식객체도 소멸자 호출이 되어야하기 때문이다.
- 대입연산자 및 복사생성자도 기본이 자동으로 뜨기때문에 지워야한다.
- 코드 외부에서 사용하게 하면 강제로 지우게 하기 위해서다.
- 복사하거나 대입하면 두 번째 인스턴스가 생길 수 있어 싱글톤의 의미가 깨지기 때문이다.
Singleton(const Singleton&) = delete; //복사 생성자의 외부 구현을 차단한다. / delete : 삭제
Singleton& operator=(const Singleton&) = delete;
스테틱 지역 변수
- 지역 변수 : 함수 호출됬을 때 생성 / 함수 끝나면 소멸
- 스테틱 지역 변수 : 지역 안에서만 접근 가능
- 차이 : 프로그램 시작했을때 메모리에 올라가고 프로그램 종료시 삭제
- 함수가 끝나도 메모리에 남아있어서 읽고 쓸 수 있다.
- 함수에 최초로 접근할 때 메모리에 올라간다.
- 왜 함수에 선언하는가? : 멀티 쓰레드는 여러개의 프로그램을 사용하기때문에 메모리에 다른 쓰레드가 접근할때 그것을 막하야한다.
- static은 멀티쓰레드의 접근을 조절하는 기능이 있다.
리소스 매니저
- friend 하는 이유
- ResourceMgr의 생성자가 외부 코드에서 사용하지 못하게 막혀있다.
- new ResourceMgr<>()를 외부에서 사용하는것을 막는다.
- Singleton 클래스는 내부적으로 new ResourceMgr<T>() 생성 작업을 해야한다.
- 정적 인스턴스를 만들기 위해 new T() 또는 T()를 호출해야한다.
- Singleton<ResourceMgr<T>> 클래스가 ResourceMgr의 생성자에 접근할 수 있어야 인스턴스를 만들 수 있다.
- 즉, Singleton 클래스에서 ResourceMgr의 멤버에 생성자를 만들기 위해 friend를 선언해주는 것이다.
- ex) T == Texture → static ResourceMgrsf::Texture instance; 호출
- 생성자가 protected니까 sington클래스는 friend가 아니면 접근이 불가능하다.
- friend 선언 필요
- 템플릿 클래스 이기에 전역 설정도 템플릿으로 생성해야한다.
friend Singleton<ResourceMgr<T>>;
};
template<typename T>
T ResourceMgr<T>::Empty;
//std::string : 경로를 키로 받을것이다
std::unordered_map<std::string, T*> resources; //리소스 저장 맵
//id: 파일 경로를 받는다.
bool Load(const std::string& id); //리소스 로드 (동적 할당) / bool : 로딩이 실패할 수 있기 때문이다.
bool Unload(const std::string& id); //리소스 언로드 (delete 할 예정)
T& Get(const std::string& id); //파일 경로를 받아서 그것을 키로 리소스 반환
map
- map<key, value> : 균형 이진 탐색 트리 자료구조
- unordered_map<key, value> : 해시 테이블 자료구조
- 둘 다 연관 컨테이너라고 분류한다.
- 템플릿 인자 2개를 받는다.
- 실제 값 : value
- key : 값을 찾을 값이다.
- 키를 이용해서 탐색하고 키를 이용해서 삭제한다.
- 연관 컨테이너 : 탐색에 최적화된 자료구조다.
- find는 효율적이지 않는다.
- 탐색을 자주해야하고, 자료의 양이 많을때 사용한다.
- 맵 변수명→first를 쓰면 key가 반환, 맵 변수명→second를 쓰면 value가 반환
- 사용처
- 정렬 필요 : 맵
- 정렬 필요 없음 : 언오더드 맵
- 사용 방법은 동일
균형 이진 탐색 트리
- 노드의 삽입과 삭제가 일어날 때 균형을 유지하도록 하는 트리다.
해시 테이블
비주얼 스튜디오 자체 에러 문제 해결
- .vs 폴더를 지움으로서 해결되는 문제들이 있다.
- 빌드 실패
- 프로젝트 설정이 이상해지거나, 빌드 구성이 이상한 경우
- 인텔리 센스 작동 안할때
- 비주얼 스튜디오가 비정상적으로 느려졌을 때
- 프로젝트가 실행은 되는데 이상한 경로에서 실행될 떄