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를 넘겨주세요.

    pointer-vs-reference

값 혹은 주소를 넘겨주는 규칙

  1. 매우 작은 객체에는 값에 의한 전달을 사용한다.
  2. 수정할 필요가 없는 큰 객체를 전달할 때 상수 참조에 의한 전달을 사용한다.
  3. 참조 인자로 전달받은 객체를 수정하기보다 값을 반환한다.
  4. 꼭 필요할 때만 참조에 의한 전달을 사용한다.

include란

  • include는 해당 소스파일에 있는 모든 소스를 그대로 복사해서 붙여넣는 것과 같다.

헤더파일

  • 헤더파일을 소스코드가 담겨있는 cpp파일에서 보통 선언부만 가져와서 기록해 놓은 파일이다.
  • 흔히 헤더파일을 메뉴판에 비유한다. cpp파일을 include해도 되지만 더 가벼운 헤더파일을 include한다고 한다.
  • 여기서 의문!

어떻게 헤더파일만 include해도 작동해?

  • 정의가 담겨있는 cpp파일도 아니고 단지 선언만 가지고 있는 헤더파일만 있는데 어떻게 작동할 수 가 있지?
  • 이 의문을 해결하려면 cpp파일이 실행될 때까지의 경로를 알아야 한다.

cpp파일은 컴파일러에 의해서 목적파일로 컴파일 된다.그 후에 링커가 라이브러리와 목적파일을 링크해 실행파일을 만들게 된다.

1번 상황

  1. A.cpp에서 B.cpp에 있는 코드를 가져다 쓰고싶다.
  2. A는 B.cpp를 include하지 않는다.
  3. 컴파일러는 A에서 B의 코드를 가져쓰는지 모르기 떄문에 오류를 낸다.

2번 상황

  1. 마찬가지로 A.cpp에서 B.cpp에 있는 코드를 가져다 쓰고싶다.
  2. A는 include “B.cpp”를 사용한다.
  3. 컴파일러와 링커는 잘 수행해서 아무 이상없이 실행파일이 생성된다.

3번 상황

  1. 마찬가지로 A.cpp에서 B.cpp에 있는 코드를 가져다 쓰고싶다. 그런데 이제 100개의 cpp파일에서도 B.cpp 코드를 가져오고 싶다.
  2. A를 포함해 총 101개 cpp파일에서 B.cpp를 include한다.
  3. include는 소스코드를 복사 붙여넣기 하는 것이다. 따라서 컴파일러는 101개의 추가된 B.cpp 파일을 아주 오랜 시간에 걸쳐 컴파일을 하게 되는 것이다.
  4. 이상 없이 실행된다.

4번 상황

  1. 마찬가지로 A.cpp에서 B.cpp에 있는 코드를 가져다 쓰고싶다. 그런데 이제 100개의 cpp파일에서도 B.cpp 코드를 가져오고 싶다.
  2. B의 선언부만 작성해 B.h 파일을 만든다. B.cpp에서 B.h를 include한다.
  3. 이제 B.cpp가 필요한 모든 101개의 CPP 파일에서 B.h를 대신 include한다.
  4. 컴파일러는 선언이 되었기 때문에 이상없이 컴파일한다.
  5. 링커는 B.h와 B.cpp가 연결되었다는 것을 확인한다. 그리고 B.h가 컴파일 된 모든 파일에게 비교적 짧은 시간에 B.cpp를 연결해준다.





© 2017. by yunsu

Powered by dolphin