C++ Principle 8편
8편
언어적 세부사항
선언과 정의의 차이
int f(int); //선언
int f(int x) // 정의
{
return x;
}
- 모든 정의는 선언이지만, 모든 선언이 정의가 되는 것은 아니다.
- 관례적으로 선언과 정의를 구분하기 위해서 정의가 아닌 선언을 선언이라고 한다.
초기화
- 모든 변수는 사용 전 초기화를 해주는 것이 좋습니다.
- string과 vector의 경우 초기화를 해주지 않아도 string은 ““로, vector는 빈 상태로 기본값이 지정되지만 그 외의 내장형 타입들은 기본값 초기화를 보장하지 않습니다.
헤더 파일
- 어딘가에 정의된 기능의 선언을 관리하는 파일을 헤더 파일이라고 합니다.
cpp 파일
- main.cpp 파일은 메인 함수가 있는 공간입니다.
- somename.cpp는 헤더 파일의 정의를 담고 있는 파일입니다.
- 헤더파일의 정의를 담고 있는 cpp파일은
#include"some_header.h"
와 같이 해당 헤더파일을 반드시 포함해야 합니다.
전역 변수
- 전역변수는 피하는 게 좋다
- 전역변수를 꼭 써야한 다면 알아보기 좋은 이름을 사용하자
int x = 2
와 같이 의미 없는 이름을 전역변수로 쓰는 건 끔찍한 일이다.
call by value
- 값을 전달할 때 새로운 변수를 생성해서 넘겨줍니다.
call by reference
void print1(vector<double> v){};
void print2(cont vector<double>& v){};
print1(v);
print2(v);
- 두 코드는 값을 복사에서 전달하느냐, 주소값을 통해서 역참조 시키느냐의 차이이다.
call by pointer vs reference
- 어떤 형태가 더 낫습니까.:
unsigned long x = 4;
void func1(unsigned long& val) {
val = 5;
}
func1(x);
void func2(unsigned long* val) {
*val = 5;
}
func2(&x);
My rule of thumb is: 주소값을 바꿔가며 포인터 연산(e.g. incrementing the pointer address to step through an array)을 할 경우에는 포인터를 사용합니다. 또한 Null 포인터를 넘겨줘야 할 경우에도 포인터를 사용합니다. 다른 모든 경우에는 reference를 넘겨주세요.
값 혹은 주소를 넘겨주는 규칙
- 매우 작은 객체에는 값에 의한 전달을 사용한다.
- 수정할 필요가 없는 큰 객체를 전달할 때 상수 참조에 의한 전달을 사용한다.
- 참조 인자로 전달받은 객체를 수정하기보다 값을 반환한다.
- 꼭 필요할 때만 참조에 의한 전달을 사용한다.
include란
- include는 해당 소스파일에 있는 모든 소스를 그대로 복사해서 붙여넣는 것과 같다.
헤더파일
- 헤더파일을 소스코드가 담겨있는 cpp파일에서 보통 선언부만 가져와서 기록해 놓은 파일이다.
- 흔히 헤더파일을 메뉴판에 비유한다. cpp파일을 include해도 되지만 더 가벼운 헤더파일을 include한다고 한다.
- 여기서 의문!
어떻게 헤더파일만 include해도 작동해?
- 정의가 담겨있는 cpp파일도 아니고 단지 선언만 가지고 있는 헤더파일만 있는데 어떻게 작동할 수 가 있지?
- 이 의문을 해결하려면 cpp파일이 실행될 때까지의 경로를 알아야 한다.
cpp파일은 컴파일러에 의해서 목적파일로 컴파일 된다.그 후에 링커가 라이브러리와 목적파일을 링크해 실행파일을 만들게 된다.
1번 상황
- A.cpp에서 B.cpp에 있는 코드를 가져다 쓰고싶다.
- A는 B.cpp를 include하지 않는다.
- 컴파일러는 A에서 B의 코드를 가져쓰는지 모르기 떄문에 오류를 낸다.
2번 상황
- 마찬가지로 A.cpp에서 B.cpp에 있는 코드를 가져다 쓰고싶다.
- A는 include “B.cpp”를 사용한다.
- 컴파일러와 링커는 잘 수행해서 아무 이상없이 실행파일이 생성된다.
3번 상황
- 마찬가지로 A.cpp에서 B.cpp에 있는 코드를 가져다 쓰고싶다. 그런데 이제 100개의 cpp파일에서도 B.cpp 코드를 가져오고 싶다.
- A를 포함해 총 101개 cpp파일에서 B.cpp를 include한다.
- include는 소스코드를 복사 붙여넣기 하는 것이다. 따라서 컴파일러는 101개의 추가된 B.cpp 파일을 아주 오랜 시간에 걸쳐 컴파일을 하게 되는 것이다.
- 이상 없이 실행된다.
4번 상황
- 마찬가지로 A.cpp에서 B.cpp에 있는 코드를 가져다 쓰고싶다. 그런데 이제 100개의 cpp파일에서도 B.cpp 코드를 가져오고 싶다.
- B의 선언부만 작성해 B.h 파일을 만든다. B.cpp에서 B.h를 include한다.
- 이제 B.cpp가 필요한 모든 101개의 CPP 파일에서 B.h를 대신 include한다.
- 컴파일러는 선언이 되었기 때문에 이상없이 컴파일한다.
- 링커는 B.h와 B.cpp가 연결되었다는 것을 확인한다. 그리고 B.h가 컴파일 된 모든 파일에게 비교적 짧은 시간에 B.cpp를 연결해준다.