Item 1

현재 C++은 다중 패러다임 프로그래밍 언어.

절차적 언어를 기본으로 객체 지향, 함수식, 일반화, 그리고 메타프로그래밍 개념까지 지원.
C, OOP 개념의 C++, 템플릿 C++, STL이 네가지가 C++을 이루고 있음.

Item 2

#define을 보다 const, enum, inline을 사용하자.

1
#define ASPECT_RATIO 1.653

ASPECT_RATIO는 컴파일러에게 넘어가기 전에, Preprocessor에서 숫자 상수로 바꾸어버린다.
즉 컴파일러를 통해 Symbol Table에 들어가지 않게 된다.
숫자 상수로 바꿔지기 때문에, file Error라도 발생하게 되면 찾기가 힘들어진다.

1
const double AspectRatio = 1.653; // 으로 사용한다.

기호 테이블에도 들어가며, 상수타입 AspectRatio은 딱 한개만 생긴다.

주의사항

  1. Constant Pointer를 사용하는 변수 / 값이 변경될수 있음.
1
2
const char* const authorName = "~~"; 
const string authorName("~~~");
  1. 클래스 맴버로 상수를 정의하는 경우
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class GamePlayer
{
private:
static const int Numturns = 5; // 상수 선언 -> Declartion이다. Define이 아니다.
int scores[Numturns]; // 상수 사용 -> 정의가 아니기 때문에 허용 X, 포인터로 동적배열
}

/*
Numturns는 cpp파일에서 정의를 해줘야 된다.
const int GamePlayer::Numturns;

또는
*/

class GamePlayer
{
private:
enum { Numturns = 5 };

int scores[Numturns];
}

// enum은 const보다 #define에 더 가까움. 그리고 #define 처럼, 어떤 형태의 쓸데없는 메모리 할당도 절대 저지르지 않음.
// 이게 템플릿 메타프로그래밍의 핵심 기법임.

매크로함수, 인라인함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b))

// 이 경우

a = 5, b = 0;
CALL_WITH_MAX(++a, b); // a가 두번 증가하게된다.
CALL_WITH_MAX(++a, b + 10); // a가 한번 증가하게된다.

template<typename T>
inline void callWithMax(const T& a, const T& b)
{
f(a > b ? a : b)
}

// 위 함수는 템플릿이기 때문에, Famliy of Functions을 만들어내고, 동일한 타입의 객체 두개를 인자로 받고 둘 중 큰것을 f에 넘겨서 호출한다.

Item 3. 낌새만 보이면 const를 들이대 보자.

1
2
3
클래스 바깥에서는 전역 혹은 네임스페이스 유효범위의 상수 선언에 사용
파일, 함수, 블록 유효범위에서 static으로 선언한 객체에도 const 가능
클래스 내부에는 정적 맴버 및 비정적 맴버 모두 상수로 선언할수 있음.
1
2
3
4
5
6
const char* p = greeting;
char * const p = greeting;
const * char * const p = greeting;

// const 키워드가 *표 왼쪽에 있다면 포인터가 가르키는 대상이 상수
// const 키워드가 *표 오른쪽에 있다면 포인터 자체가 상수

함수 반환 값을 상수로 정해주면, 안전성이나 효율을 포기하지 않고도 사용자측의 에러 돌발 상황을 줄이는 효과가 있게 된다.

상수 맴버 함수

해당 맴버 함수가 상수 객체에 대해 호출될 함수이다.

장점

  1. 클래스의 인터페이스를 이해하기 좋게 한다.

    C++의 실행 성능을 높이는 핵심 기법 중 하나가 상수 객체에 대한 참조자로 진행.

  • 비트수준 상수성

    • 어떤 맴버 함수가 그 객체의 어떤 데이터의 맴버도 건드리지 않아야 된다. 그 함수가 const임을 인정하는 개념
  • 논리적 상수성

    • 비트수준 상수성에서 통과하는 상황을 보완하는 개념. 상수 맴버 함수라고 해서 객체의 한 비트도 수정할수 없는 것이 아니라, 일부 몇비트는 바꿀수 있고, 사용자 측에서 알아채지 못하면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
class CTextBlock
{
public:
std::size_t length() const;
private:
char *pText;

mutable std::size_t textlength;
mutable bool lengthIsValid;

std::size_t CTextBlock::length() const
{
if(!lengthIsValid)
{
textLength = std::strlen(pText);
lengthIsValid = true;
}
return textlength;
}
}

class TextBlock
{
public:
const char& operator[](std::size_t position) const
{
~~~~~
~~~~~

return text[position];
}

char& operator[](std::size_t position)
{

~~~~~
~~~~~

return text[position];
}
}

// 코드 중복을 막기 위해서 const_cast을 활용한다.

class TextBlock
{
public:
const char& operator[](std::size_t position) const
{

~~~~~
~~~~~

return text[position];
}

char& operator[](std::size_t position)
{
return const_cast<char &>(static_cast<const TextBlock&>(*this)[position]);
// static_cast<const TextBlock&>(*this)[position]) const을 붙여서 const char& operator[]함수를 호출한다.
// const_cast<char &> const char&에 const을 제거 시키고 리턴.
}
}

정리

  1. const을 붙여 선언하면 컴파일러가 사용상의 에러를 잡아내는데 도움을 준다. const는 어떤 유효범위에 있는 객체에도 붙을수 있고, 함수 매개변수 및 반환 타입에도 붙을수 있으며, 맴버 함수에도 붙을수 있음.
  2. 컴파일러 쪽에서 비트수준 상수성을 지켜야되지만, 우리는 논리적인 상수성을 사용해서 프로그래밍한다.
  3. 상수 맴버 및 비상수 맴버함수가 기능적으로 서로 똑같이 구현되어 있을경우, 코드 중복을 피하는데, 비 상수버전이 상수버전을 호출하게 만든다.

Item 4. 객체를 사용하기 전에 반드시 그 객체를 초기화한다.

기본제공 타입의 객체는 직접 손으로 초기화한다. 경우에 따라 저절로 되기도 하고 안되기도 하기 때문

생성자에는 데이터맴버에 대한 대입문을 생성자 본문에 넣는 방법으로 맴버를 초기화 하지말고, 맴버초기화 리스트를 즐겨 사용하자.
그리고 초기화 리스트에 데이터를 나열할때는 클래스에 각 데이터 맴버가 선언된 순서와 똑같이 나열한다.

여러 번역 단위에 있는 비지역 정적 객체들의 초기화 순서 문제는 피해서 설계해야된다.
비지역 정적 객체를 지역 정적 객체로 바꾸면 된다. -> SingleTon Pattern

정적 객체는 자신이 생성된 시점부터 프로그램이 끝날때 까지 살아 있는 객체를 말한다.

  1. 전역 객체
  2. 네임스페이스 유효범위에서 정의된 개체
  3. 클래스 안에서 static 으로 선언된 객체
  4. 함수 안에서 static으로 선언된 객체
  5. 파일 유효범위에서 static으로 정의된 객체

이들 중 함수 안에 있는 정적 객체를 지역 정적 객체, 나머지를 비지역 정적 객체라고 한다.

번역 단위(Transation Unit) 는 컴파일을 통해 하나의 목적 파일(Object File)을 만드는 바탕 소스코드를 말한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 비정적 정적 객체의 사용법

// Header
extern float g_Data_Position[10][2];
extern int g_Data_HP;

extern int g_Data_HitDamage;

// Source
float g_Data_Position[10][2] = { { 10, 10 },{ 10, 150 },{ 145, 150 },{ 140, 95 },{ 140, 40 },{ 61, 40 },{ 36, 96 },{ 114, 66 },{ 64, 12 },{ 63, 154 } };
int g_Data_HP = 20;
int g_Data_HitDamage = 5;

// 바로 초기화를 해줘야 된다.

// 비지역 정적 객체를 지역정적 객체로 바꿀려면 SingleTon 사용한다.