3.1 배열 사용
- 배열에 게임 엔트리 저장
- 배열에 객체들을 저장하는 것이 배열의 흔한 용도
- 그 외 병원에 환자들의 정보를 등록하는 것, 축구팀의 선수 이름을 등록하는 것..
- 상위 점수 엔트리를 표현하는 객체에 무엇을 포함할 것인지를 생각한다
- 가장 간단히 떠오르는 건 score과 이름일 것이다.
class GameEntry
{
public:
GameEntry(const string& n = "", int s = 0); // 이름과 점수를 받는다.
string getName() const; // getter
int getScore() const; // getter
private:
string name; // 이름
int score; // 점수
};
- 상위 점수 클래스 작성
- Scores 클래스는 배열 entries의 상위 점수들을 저장한다.
- 상위 점수의 최대 개수는 인스턴스마다 다를 수 있으므로, 원하는 최대값을 정의하는 maxEntries 멤버 변수를 만든다.
- 엔트리의 실제 개수를 관리하기 위해 numEntries를 만든다.
class GameEntry;
class Scores
{
public:
Scores(int maxEnt = 10); // 생성자에서 최대 값 받아서 new로 메모리 할당
~Scores(); // 소멸될 때 소멸자에서 소멸
void add(const GameEntry& e); // Score Board에 추가하기
GameEntry remove(int i) // i번째 위치에 있는 Entry 제거
throw(out_of_range);
private:
int maxEntries; // 엔트리 최대 값
int numEntries; // 현재 엔트리의 개수
GameEntry* entries; // 엔트리 배열
};
- 생성자 코드 작성
- 생성자는 배열의 최대 크기를 저장하고 배열 공간을 할당한다.
- 그리고 배열 내부 개수를 0으로 대입하여 초기화한다.
- 소멸자 코드 작성
- 소멸자는 생성자에서 할당한 배열을 해제한다.
Scores::Scores(int maxEnt)
{
maxEntries = maxEnt;
// maxEntries는 엔트리의 최대 개수이다.
entries = new GameEntry[maxEntries];
// entries는 GameEntry의 배열이고, 최대 maxEntries의 수만큼 저장된다.
numEntries = 0;
// numEntries는 현재 들어온 엔트리의 개수이며, 생성자에서 0으로 초기화한다.
}
Scores::~Scores()
{
delete[] entries;
// 생성자에서 할당된 entries를 소멸자에서 해제한다.
}
- 비디오 게임을 할 수록 add 함수를 통해 추가적인 GameEntry 객체들이 배열에 복사됨
- 나중에 추가될 remove 함수를 통해 i번째에 있는 GameEntry 객체를 제거할 수 있음
- 범위는 0 <= i <= numEntries - 1이어야 하고, 이를 초과하면 IndexOutOfBounds 예외를 발생시킨다.
- add 멤버 함수 작성
void Scores::add(const GameEntry& e)
{
// 배열이 꽉 차있는지 검토한다.
int newScore = e.getScore();
// Entries가 꽉 찼을 경우
if (numEntries == maxEntries) {
// 만약 새로 들어올 GameEntry의 점수가 현재 Entry의 가장 작은 요소보다 작을 경우
if (newScore <= entries[maxEntries - 1].getScore())
return;
}
// 꽉 찬게 아니라면 요소를 추가한다.
else
numEntries++;
// numEntries++를 했으므로 numEntries - 2가 현재 마지막 요소
// entries[i+1] = entries[i]; 이렇게 이동하므로
// numEntries - 2부터 이동해야 한다.
int i = numEntries - 2;
// newScore가 entries[i]보다 작을때까지 반복
// ex) 3[440] 4[420] , 넣어야 할 값 e: 430
// 인덱스가 4일 때, 아래 반복문이 실행되고, 4에 있던 값이 5로 들어간다.
// 인덱스가 3이 되면 430 > 440이라서 루프를 탈출한다.
while (i >= 0 && newScore > entries[i].getScore())
// i >= 0인 이유는 인덱스가 0 미만으로 내려갈 수 없기 때문
{
entries[i + 1] = entries[i]; // 위에서 설명했던 부분
i--;
}
// i + 1에 삽입하는 이유는? 빈 공간인 4번 인덱스에 넣어줘야 하므로.
entries[i + 1] = e;
}
- remove 함수 작성
GameEntry Scores::remove(int i) throw(out_of_range)
{
// i가 정상 범위 안에 있는지 검사
if ((i < 0) || (i >= numEntries)) // i가 0보다 작거나 numEntries보다 같거나 큰지
throw out_of_range::exception("Invalid Index");
GameEntry e = entries[i]; // 정상적인 범위 안의 i이므로 가져온다.
// i번째에 있는 entry를 덮어쓰며 맨 마지막 엔트리까지 앞으로 1칸씩 이동
// i가 3이라고 가정하면
for (int j = i + 1; j < numEntries; j++) // 루프는 4로 시작
{
entries[j - 1] = entries[j]; // 4번째에 있는 값을 3번째로 덮어쓴다.
}
numEntries--; // 총 개수를 1개 줄임
return e;
}
- remove의 구현은 add와 비슷하고, 단지 반대의 과정이다.
- add는 요소를 추가하기 위해 뒤로 미는 연산인데,
- remove는 제거된 요소의 빈 자리를 채우기 위해 앞으로 1칸씩 당기는 연산이다.
- e의 존재는 제거된 위치의 entries를 반환하기 위해 존재한다.
마무리하며..
- 이 함수들은 더 정교한 자료구조를 만들기 위해 반복적으로 사용될 기술들의 기초.
- 모든 자료구조가 실제적인 방법들을 통해 구현되어야 하므로 이 구조를 이해하는게 도움이 된다.
'자료구조와 알고리즘 > [서적]C++로 구현하는 자료구조와 알고리즘' 카테고리의 다른 글
3장-4. 일반적인 링크드 리스트 구현 (단일, 이중, 환형) (0) | 2022.09.12 |
---|---|
3장-3. 단일 링크드 리스트 (String 버전) (0) | 2022.09.11 |
3장-2. 2차원 배열과 위치 게임 (0) | 2022.09.11 |
3장-1. 배열 정렬 (0) | 2022.09.11 |
2장. 객체지향 설계 - 목표와 원리, 그리고 패턴 (0) | 2022.09.05 |
댓글