
게임 개발자를 위한 C++ 문법
템플릿
함수 오버로딩
- C++에서는 함수 이름과 매개변수 타입 정보를 함께 사용해 함수를 구분한다.
- 함수 이름 구분을 위해 내부적으로 고유한 이름을 부여하는것을 네임 맹글링이라고 한다.
- 함수이름이 같고 -> 매개변수 타입이 다른경우, 매개변수의 개수가 다른경우 오버로딩
오버로딩이 안되는 경우
- 타입 변환이 가능한 매개변수로 인해 두 개 이상의 오버로딩된 함수가 호출 후보가 되는 경우
- 디폴트 매개변수로 인해 함수 호출 형태가 중복되는 경우
- 매개변수의 타입만 포인터와 배열로 다른 경우
- 함수의 반환 타입만 다른 경우
오버로딩의 순서
-
정확한 매개변수 타입 일치
-
타입 승격 변환 : 값이 손실되지 않는 방향으로 변환하는것을 승격이라고 함
- char or short -> int
- float -> double
- bool -> int
-
표준 타입 변환 : 승격보다는 광범위, 값 손실이 발생하는 경우도 있다.
- int -> double
- double -> int
- double -> float
-
사용자 정의 타입 변환 : 클래스 타입의 변환 함수나 생성자 등을 통해 이뤄지는 변환
템플릿
- 타입에 관계없이 일반화된 코드를 작성하기 위한 문법
- template
- 어떤 타입이 올지 모르나 그 타입을 T로 부르겠다.
템플릿 클래스
함수 뿐아니라 클래스도 템플릿으로 사용해 일반화할 수 있습니다.
숙제
STL 기초
STL은 C++ 표준 라이브러리의 일부로, 컨테이너, 알고리즘, 반복자 등의 템플릿 기반 구성 요소를 포함합니다.
컨테이너
- 모든 컨테이너는 템플리승로 구현되어 있으므로, 다양한 타입의 데이터를 저장할 수 있습니다.
- 모든 컨테이너는 메모리 관리를 내부적으로 합니다. 따라서 사용 시 메모리 해제를 직접 고려하지 않아도 됩니다.
- 대부분 컨테이너는 반복자를 제공, 따라서 내부 구현을 몰라도 동일한 방식으로 컨테이너를 순회할 수 있습니다.
벡터
- 템플릿 클래스로 구현되어 특정 타입에 종속되지 않음
- 삽입되는 원소 개수에 따라 내부 배열의 크기가 자동으로 조정
- 임의 접근이 가능(인덱스를 통해 특정 위치에 접근)
- 삽입/삭제는 맨 뒤에 하는 게 좋습니다. (중간 삽입/삭제는 배열 복사가 필요하므로 비효율적)
vector<int> vec3 = {1, 2, 3, 4, 5};
vector<int> vec4(vec3);
//vector<int> vec4 = vec3 하면 대입
- 2차원 배열처럼 벡터 사용, 벡터의 타입을 벡터로 하면 된다.
vector<vector<int>> vec2D(3, vector<int>(4, 7)); // 3x4행렬, 모든 원소가 7로 초기화
// 두 번째 요소 제거
vec.erase(vec.begin() + 1);
// 2~4 번째 요소 제거
vec.erase(vec.begin()+1, vec.begin()+3);
맵
- 키-값 쌍 형태로 저장
- 키값을 기준으로 오름차순 자동 정렬
- 중복된 키값 허용 X
myMap[5] = "Apple";
myMap.insert(make_pair(1, "Banana"));
myMap.insert({2, "Orange"});
-
insert()와 make_pair()를 함쎄 사용해서 값 추가 가능
-
{}나 []로 추가도 가능
-
find() : 특정 key가 map에 존재하는지 확인가능
-
size() : 키-값 쌍의 개수를 반환
-
erase(key) : 특정key를 가진 요소 삭제, 삭제된 요소의 개수를 반환하는데 map은 키중복이 안되므로 삭제 성공하면 1, 실패하면 0이 반환된다.
-
myMap.clear() : 모든 요소 삭제
알고리즘
- 다양한 컨테이너와 독립적으로 동작하는 범용 알고리즘을 제공
- 반복자덕에 컨테이너의 요소를 추상화하여 일관된 방식으로 접근할 수 있게함
- find(first, last, 찾을 값) : 컨테이너 내부에서 특정 원소를 찾아 해당 원소의 반복자를 반환하는 함수
int arr[] = {1, 2, 3, 4, 5};
int size = sizeof(arr) / sizeof(arr[0]);
// 특정 값 4를 찾음
auto it = find(arr, arr + size, 4);
if (it != arr + size) {
cout << "값 4가 배열에서 발견됨, 위치: " << (it - arr) << endl;
} else {
cout << "값 4가 배열에 없음" << endl;
}
...
string str = "hello world";
// 문자열 o찾기
auto it = find(str.begin(), str.end(), 'o');
if (it != str.end()) {
cout << "문자 'o'가 문자열에서 발견됨, 위치: " << (it - str.begin()) << endl;
} else {
cout << "문자 'o'가 문자열에 없음" << endl;
}
반복자
- 컨테이너의 내부 구조는 서로 다르지만, 대부분 알고리즘을 동알한 코드를 활용해서 사용할 수 있었다.
- 반복자를 기반으로 알고리즘이 동작하기 때문
- 반복자는 컨테이너의 요소에 대한 일관된 접근 방법을 제공하므로, 알고리즘이 특정 컨테이너의 내부 구현과 무관하게 동작할 수 있습니다.
정방향 반복자
- begin()은 컨테이너의 첫 번째 원소를 가리키는 반복자, end()는 마지막 원소 다음을 가리키는 반복자
- end()가 마지막 원소 다음인 이유는 일관된 반복구조를 유지하고, 탐색 실패를 쉽게 표현할 수 있기 때문입니다.
역방향 반복자
- rbegin()은 컨테이너의 마지막 원소를 가리키는 역방향 반복자
- rend()는 컨테이너의 첫 번째 원소 이전을 가리키는 역방향 반복자
숙제
객체지향적 설계
객제지향적 코드를 구현하는 능력 배양
- 대부분 라이브러리 및 오픈소스는 객체지향적으로 설계되어 있음
- 좋은 설계로 구현된 코드는 개발 시간을 단축할 수 있다.
- 좋은 설계로 구현된 코드는 기능 변경에 유연하게 대응할 수 있다.
응집도
- 클래스 또는 모듈 내부의 구성 요소들이 얼마나 밀접하게 관련되어 있는지를 나타냄
- 응집도가 높을수록 좋은 설계라고 평가
- 클래스 내부에 관련 없는 기능들이 포함되어 있으면, 변경이 자주 발생하고, 확장하기도 쉽지 않다.
응집도가 낮은 경우
- 서로 관련없는 기능들이 하나의 클래스에 포함된 경우를 말함
목표가 피자배달인 경우
- 피자배달
- 웹사이트 디자인
- 회사 마케팅
- 창고관리
이런 기능들이 한 클래스에 포함되어 있다면 응집도가 낮다고 할 수 있다.
응집도가 높은 경우
- 서로 관련있는 모듈만 하나의 class에 있는 경우
- 피자 배달 경로 확인
- 주문한 고객 대응
- 배달 예상 시간 측정
결합도
- 결합도는 모듈 또는 클래스 '간'의 의존성을 말합니다.
- 일반적으로 결합도는 낮을수록 좋은 코드
- 결합도가 높으면 하나의 모듈이 변경될때 다른 모듈들에도 영향이 갑니다.
SOLID 원칙
- 유지보수성 및 확장성 향상
- 변경에 유연한 설계 제공
단일 책임 원칙(SRP)
개방 폐쇄 원칙(OCP)
- 확장에는 열려있어야하고 수정에는 닫혀있어야 한다.
리스 코프 치환 원칙(LSP)
- 자식 클래스는 부모 클래스에서 기대되는 행동을 보장해야 한다.
- 부모 클래스를 사용하는 코드가 자식 클래스로 대체되더라도 정상적으로 동작해야 한다.
인터페이스 분리 원칙(ISP)
- 클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다.
- 하나의 거대한 인터페이스보다는 역할별로 세분화된 인터페이스를 만들어 필요한 기능만 구현하도록 설계해야한다.
의존 역전 원칙(DIP)
- 고수준 모듈은 저 수준 모듈에 직접 의존하는 것이 아니라, 두 모듈 모두 추상화에 의존해야 한다.
- 인터페이스나 추상 클래스 같은 추상화 계층을 두어 결합도를 낮추는것이 좋은 설계
숙제 1.
숙제 2.
2주차 숙제
1번과제