Post

[ STUDY/Effective C++ ] 2022. 6. 18. 01:48

#define 소스코드의 경우에는 컴파일러에게 넘어가기 전, 선행처리자에서 숫자 상수로 바꾸어버린다.

그래서 #define pie 3.141 이라면

pie라는 이름은 컴파일러가 쓰는 기호 테이블에 들어가지 않고 숫자 상수(3.141)로 대체된다.

 

CONST

하지만 #define대신에 const를 사용해

const double pie = 3.141; 을 작성한다면 상수타입의 데이터이기 때문에 컴파일러에서도 보이게된다. 

 

그리고 매크로인 #define을 쓰게된다면 3.141의 사본이 등장횟수만큼 코드안에 들어가지만, 상수 타입인 const를 쓰면 사본이 한개만 생성되어 최종 코드의 파일 크기가 더작아지게된다. 

 

 

※ #define을 상수로 교체할때의 주의할 점

1. 상수 포인터를 정의하는 경우

포인터는 꼭 const로 선언, 포인터가 가리키는 대상까지 const로 선언

const char * const alphabet = "ABCD";
const std::string alphabet = "ABCD";//위보다 더 좋은방법

 

2. 클래스 멤버로 상수를 정의하는 경우

Class GamePlayer{
private:
	static const int  NumTurns = 5; // 상수선언
	int scores[NumTurns];
	...
};

3번째 줄의 코드는 '정의'가 아닌 선언이다. 정적 멤버로 만들어지는 정수류 타입의 클래스 내부 상수는 정의 없이 선언만 해도 아무 문제없다.

const int GamePlayer::NumTurns; //NumTurns정의

하지만 컴파일러가 정의를 달라고 떼쓰는 경우에는 위와 같은 클래스 상수 정의를 '구현파일'에 둔다.

선언된 시점에서 상수의 초기값이 주어지기때문에, 정의에서는 초기값이 주어지면 안된다.

 

#define으로는 클래스 상수를 정의할 수도, 캡슐화 혜택도 받을 수 없다.

 

class CostEstimate{
private:
	static const double FudgeFactor; //정적 클래스 상수 선언(헤더파일에 두기)
	...
};
const double
	CostEstimate::FudgeFactor = 1.35; //정적 클래스 상수 정의(구현파일에 두기)

오래된 컴파일러의 경우에는 반대로 선언된 시점에서 초기값을 주는게 아닌, 정의 시점에 초기값을 주도록해야한다.

 

 

ENUM

여기서 예외)

Class GamePlayer {
private:
	enum {NumTurns = 5 }; //5에대한 기호식 이름으로 만듬
    
    int scores[NumTurns];
    ...
};

오래된 컴파일러를 배려해서 '나열자 둔갑술'(enum hack) 기법을 활용하자.

나열자 타입의 값은 int가 놓일 곳에도 쓸 수 있다. 원래라면 클래스내 초기화 금지로인해 위의 int scores[NumTurns]같은 배열멤버선언은 오래된 컴파일러에서 동작하지못한다.

 

 

INLINE

#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b)) //#define을 쓴 경우


template<typename T> //inline에 대한 Template을 쓴 경우
inline void callWithMax(const T& a, const T& b)
{
	f(a>b ? a:b);
}
CALL_WITH_MAX(++a,b);
CALL_WITH_MAX(++a,b+10);

매크로를 작성할 때는 매크로 본문에 들어 있는 인자마다 반드시 괄호를 씌워주어야한다.

하지만 inline을 쓴다면 그런 괄호들을 굳이 씌울 필요가 없다.

 

이런 눈에 보이는 부분들이 아니더라도

밑의 코드들을 보았을때 #define매크로를 사용한다면 f가 호출되기도 전에 a가 증가하는 횟수가 달라지게 된다. 

하지만 inline을 쓴다면 인자를 여러번 평가할지 모른다는 걱정도 없다.

그리고 매크로가 아닌 진짜 함수이기때문에 함수의 유효범위 및 접근규칙을 그대로 따라가기에 기존 매크로의 효율유지는 와 함께 함수의 모든 동작방식 및 타입의 안정성까지 챙길 수 있다.

 

 

 

 

Post

[ STUDY/Effective C++ ] 2022. 6. 13. 21:30

어떻게하면 C++을 잘 이해했다고 소문이 날까?

C++를 단일 언어로 바라보는것에서 눈을 넓혀  여러 언어들의 연합체로 보자.

 

그러고나서 각 언어에 관한 규칙을 파악해가면 시각이 단순해지고 명확해지며, 기억하기도 편해진다.

 

C++의 하위언어는 총 4가지이다.

 

- C : C++의 기본베이스

"값 전달이 참조 전달보다 대개 효율이 더 좋다" 라는 규칙이 통함

 

- 객체 지향 개념의 C++ : 클래스를 쓰는 C

사용자 정의 생성자/소멸자 개념이 생김

상수 객체 참조자에 의한 전달 방식이 효율이 더 좋다 (위 C와 반대됨)

 

-템플릿 C++ : C++의 일반화 프로그래밍 부분

(위 객체 상수 객체 참조자에 의한 전달 방식의 효율이 더 두드러짐)

 

- STL : 대단히 특별한 템플릿 라이브러리

반복자와 함수 객체가 C의 포인터를 본따 만들었다를 알게됨

-> STL의 반복자 및 함수 객체에 대해서는 값 전달에 대한 규칙이 다시 통함 ( 객체지향개념C++, 템플릿 C++와 반대)

 

이렇게 각 하위언어마다 규칙이 다르기때문에 효과적인 프로그램 개발을 위해서는 한 하위 언어에서 다른 하위언어로 옮겨가면서 대응전략을 바꾸어야한다.

 

 

▲ top