6.1 함수 기초
● 함수 정의 : 반환 타입, 이름, 없거나 하나 이상인 매개변수 목록, 본체로 구성
● 함수 본체 : 매개변수는 쉼표로 구분한 목록을 괄호로 둘러싸 지정하고 함수에서 수행하는 행동
● 함수는 괄호 쌍인 호출 연산자를 통해 실행하는데, 이 연산자에서는 함수나 함수에 대한 포인터인 표현식을 취한다.
● 함수 만들기
○ 계승 : 반복문과 제귀로 만들 수 있음
● 함수 호출하기
○ 호출하는 함수의 실행을 잠시 멈추고 호출 되는 함수의 실행을 시작한다.
● 매개변수와 인자
○ 인자는 함수 매개변수에 대한 초기값이다. 인자를 평가하는 순서는 보장하지 않는다. 컴파일러에 따라 다르다.
● 함수 매개변수 목록
○ 함수 매개변수 목록은 비어있을 수는 있지만 생략 x. int func(void) {}로도 가능하다.
○ 두 매개변수 이름이 같을 수는 없다. 함수의 가장 바깥 유효범위에 있는 지역변수는 매개변수와 이름이 달라야한다.
○ 매개변수의 이름은 선택적이지만 이름이 없는 매개변수를 사용할 방법은 없다.
● 함수 반환 타입
○ void타입을 포함한 대부분의 타입을 사용할 수 있다.
○ 배열 타입이나 함수 타입은 반환 타입으로 사용할 수 없다. 이것은 배열 또는 함수에 대한 포인터로 반환 할 수 있다.
6.1.1 지역 객체
● 이름에 유효 범위가 있으며 객체에는 수명이 있다.
○ 이름의 유효 범위는 프로그램 본문에서 해당 이름을 볼 수 있는 부분이다.
○ 객체의 수명은 프로그램을 실행하는 동안 객체가 존재하는 시간이다.
● 함수 본체 안에서 정의한 매개변수와 변수를 지역 변수라 한다.
● 모든 함수 밖에서 정의한 객체는 프로그램을 실행하는 동안 내내 존재한다.
● 자동 객체
○ 구역을 실행하는 동안에만 존재하는 객체
○ 매개변수도 포함된다.
● 지역 static 객체
○ 수명이 함수 호출을 너머 지속하는 지역변수
○ 초기값이 없으면 0으로 만든다.
6.1.2 함수 선언
● 함수를 결코 사용하지 않는 한, 그 함수를 정의하지 않아도 선언 할 수 있다. 이에 대해서는 15.3절에서 다룬다.
● 함수 선언에는 본체가 없으므로 매개변수 이름이 없어도 된다.
● 함수 선언은 함수 원형이라고도 한다.
● 함수 선언은 헤더 파일에 둔다.
○ 선언은 헤더 파일에 두고 정의는 소스 파일에 하는 것이 좋다.
6.1.3 분리 컴파일
● 분리 컴파일 : 프로그램을 여러 파일로 나눌 수 있으며 그 각각을 독립적으로 컴파일 할 수 있다.
● 여러 소스 파일을 컴파일과 링크하기
○ c옵션을 통해 오브젝트 파일 을 만들고 이것을 이용해서 실행파일을 만든다.
6.2 인자 전달
● 매개변수 초기화는 변수 초기화와 같은 방식으로 작동한다.
● 매개변수가 참조자이면 해당 인자를 "참조로 전달"한다고 하거나 그 함수를 "참조로 호출"한다고 한다.
● 인자 값을 복사할 떄 매개변수와 인자는 독립적인 객체이다. 이런 인자를 '값으로 전달' 한다고 하거나 다른 말로 그 함수를 '값으로 호출'한다고 한다.
6.2.1 값에 의한 인자 전달
● 값으로 인자를 전달하면 원래 변수에 영향을 미칠 수 없다.
● 포인터 매개변수
○ 포인터는 비참조자 타입처럼 행동한다.
○ 일반적으로 c에서는 포인터 매개변수를 사용해 함수 외부 객체에 접근하지만 c++에서는 그 대신 참조자 매개변수를 사용한다.
6.2.2 참조자로 인한 전달
● 매개 변수는 종종 함수에서 인자 값 하나 이상을 변경하는데 사용한다.
● 참조자를 사용해 복사 피하기
○ 크기가 큰 클래스 타입이나 컨테이너 객체를 복사하는 것은 비효율적일 수 있다. 그래서 참조자를 사용해 이를 피할 수 있다.
○ 함수 내에서 변경하지 않는 참조자 매개변수는 const에 대한 참조자여야 한다.
● 참조자 매개변수를 사용해 추가 정보 반환하기
○ 함수에서는 값을 단 하나만 반환할 수 있다. 하지만 하나 이상을 반환해야할 때도 있는데 그때는 참조자 매개변수를 사용하면 된다.
6.2.3 const 매개변수와 인자
● 상위 const인 매개변수에 const와 const가 아닌 객체 어느 것이든 전달할 수 있다.
● 포인터 또는 참조자 매개변수와 const
○ 매개변수는 변수 초기화와 같은 방식으로 초기화하므로 일반적인 초기화 규칙을 기억하는 것이 도움이 될 수 있다.
● 가능하면 const에 대한 참조자르 사용한다.
6.2.4 배열 매개변수
● 배열은 복사할 수 없으며 배열을 사용할 떄 그 배열은 (일반적으로) 포인터로 변환한다.
● 각 함수 선언에는 매개변수가 const int* 타입 하나이다.
// 외양과 달리 print에 대한 이 세 가지 선언은 같다.
// 각 함수에는 매개변수가 const int* 타입 하나이다.
void print(const int*);
void print(cont int[]); // 함수에서 배열을 취한다는 의도를 나타낸다.
void print(cont int[10]); // 문서화 목적을 표시한 차원
● 표시자를 사용한 배열 범위 지정하기
void print(const char *cp) {
if (cp) // cp가 널 포인터가 아니면
while (*cp) // cp에서 가리키는 문자가 널문자가 아닌 동안
cout << *cp++; // 해당 문자를 출력하고 포인터를 증가 시킨다.
}
○ 데이터에 (널 문자 처럼) 일반적인 데이터와 구별할 수 있는 끝 표시자를 포함하는 방법
● 표준 라이브러리 변환 사용하기
○ 배열의 첫 요소와 마지막 요소 바로 다음에 대한 포인터를 전달하는 것이다.
void print(const int *a, cont int *b)
{
// a에서 시작해 b를 제외한 모든 요소를 출력한다.
while ( a != b)
cout << *a++ << endl;
}
● 크게 매개변수를 명시적으로 전달하기
○ 배열 크기를 나타내는 두 번째 매개변수를 정의하는 것이다.
// const int ia[]는 const int* ia와 같다.
// size를 명시적으로 전달하고 ia 요소에 접근을 제어하는 데 사용한다.
void print(const int ia[], size_t size) {
for ( size_t i = 0; i != size; i++) {
cout << ia[i] << endl;
}
}
● 배열 매개변수와 const
○ 함수에서 요소 값을 변경해야할 경우에만 매개변수를 const가 아닌 타입에 대한 보통의 포인터로 사용해야 한다.
● 배열 참조자 매개변수
○ 매개변수도 배열에 대한 참조로 정의할 수 있다.
// 좋음 : 매개변수는 배열에 대한 참조자이므로 차원은 타입의 일부분이다.
void print(int (&arr) [10]) {
for (auto elem : arr) {
cout << elem << endl;
}
}
● 다차원 배열 전달하기
○ 다차원 배열 역시 첫 요소에 대한 포인터로 전달한다.
○ 두 번째 이하의 차원 크기는 해당 요소 타입의 일부분이므로 반드시 지정해야 한다.
○ 배열 문법을 사용해 함수를 정의할 수도 있는데, 일반적으로 컴파일에서는 첫 번재 차원을 무시하므로 포함하지 않는것이 좋다.
6.2.5 main : 명령행 옵션 처리하기
● int main(int argc, char **argv[]) {...}
● 인자를 main에 전달하면 argv의 첫 요소에서는 프로그램 이름이나 빈 문자열을 가리킨다. 그 이하 요소는 명령행에서 제공한 인자를 전달하면 마지막 포인터 바로 다음 요소는 0임을 보장한다.
● argv 내 인자를 사용할 떄는 선택인자가 argv[1]에서 시작하며 argv[0]에는 사용자 입력이 아니라 프로그램 이름이 담겨 있다는 점을 기억한다.
6.2.6 매개변수가 가변인 함수
● 가변 인자 수를 취하는 함수를 만들기 위한 두가지 기본적인 방법
○ 모든 인자 타입이 같으면 initializer_list 라이브러리 타입을 전달할 수 있다.
○ 인자 타입이 다양하면 가변인자 템플릿이라고 하는 특별한 함수를 만들 수 있다.
○ 개수가 가변인 인자를 전달하는 데 사용할 수 있는 특별한 매개변수 타입인 매개변수 생략도 있다. ->
이 방법은 프로그램에서 c함수와 인터페이스 할 때만 사용하는 것이 좋다.
● initializer_list 매개변수
○ 이 요소는 항상 const이므로 요소값을 변경할 수 없다.
○ initializer_list는 지정한 타입의 값을 요소로 하는 배열을 나타내는 라이브러리 타입이다.
void error_msg (initializer_list<string> il) {...}
● 매개변수 생략
○ c 라이브러리인 varargs를 사용하는 c코드와 프로그램을 인터패이스할 수 있다.
○ void foo(parm_list, .... ); 등으로 사용
6.3 반환 타입과 return 문
6.3.1 반환 값이 없는 함수
● void 타입의 함수에서만 사용할 수 있다.
6.3.2 값을 반환하는 함수
● 반환 값은 반환 타입과 타입이 같거나 그 타입으로 암시적 변환을 할 수 있어야 한다.
● 값을 반환하는 법
○ 값은 변수와 매개변수를 초기화하는 것과 정확히 같은 방식으로 반환한다.
● 지역 객체에 대한 참조자나 포인터는 절대 반환하지 않는다.
○ 지역 객체에 대한 포인터를 반환하는 것도 잘 못 된 것이다.
-> 이유 : 일단 함수를 마치면 지역 객체는 해제하므로 포인터에서는 존재하지 않는 객체를 가리킨다.
● 클래스 타입을 반환하는 함수와 호출 연산자
○ 호출 연산자 역시 결합법칙과 우선순위를 따른다. 호출 연산자는 점, 화살표연산자와 우선 순위가 같고 이 연산자와 마찬가지로 왼쪽 결합이다.
● 참조자 반환은 좌변 값이다.
○ 참조자를 반환하는 함수에 대한 호출은 좌변 값이지만 다른 반환 타입은 우변 값을 반환한다.
○ 반환 타입이 const에 대한 참조자이면 (일반적으로) 그 호출 결과에 대입할 수 없다.
● 반환 값 목록 초기화
○ 중괄호로 둘러싼 값 목록을 함수에서 반환할 수 있다.
○ 함수 반환을 나타내는 임시 객체를 초기화 할 수 있다.
● main에서 반환
○ main함수에서는 반환하지 않고 종료할 수 있다. 컴파일러가 자동으로 추가한다.
○ 반환한 값은 상태 표시자로 취급한다. cstdlib헤더에서 성공과 실패를 나타내는 데 사용할 수 있는 전처리기 변수를 정읳나다.
● 재귀
○ 직간접적으로 자신을 호출하는 함수를 재귀함수라고 한다.
○ main 함수에서는 자신을 호출 할 수 없다.
6.3.3 배열에 대한 포인터 반환하기
● 배열은 복사 불가능하여 함수에서는 배열을 반환 할 수 없다. 하지만 함수에서는 배열에 대한 포인터나 참조자는 반환할 수 이다.
● 타입 별칭 사용하기
typedef int arrT [10]; // arrT는 int가 10개인 배열 타입과 동의어이다.
using arrT = int[10]; // arrT 선언과 같으며 2.5.1 절을 참고한다.
arrT* func (int i); // func에서는 int가 10개인 배열에 대한 포인터를 반환한다
● 배열에 대한 포인터를 반환하는 함수 선언하기
○ 타입 별칭을 사용하지 않고 func을 선언하려면 정의하는 이름 뒤에 배열 차원이 있어야한다.
타입 (*함수(매개변수 목록)) [차원]
● 후행 반환 타입
○ 후행 반환 타입은 매개변수 목록 다음에 오고, 이앞에는 ->를 둔다.
○ 일반적으로 반환 타입을 두는 곳에 auto를 둔다.
auto func(int i) -> int (*)[10];
● decltype 사용하기
○ 함수에서 반환하는 포인터에서 가리키는 배열을 알 수 있으면 decltype을 사용해 반환 타입을 선언할 수 있다.
int a[] = {1,2};
decltype(a) *arrPtr(int i) {}
6.4 함수 다중 정의
● 이름은 같지만 매개변수가 다르며 같은 유효 범위 내에 있는 함수를 다중 정의 했다고 한다.
● 이 함수를 호출할 때 컴파일러에서는 전달하는 인자 타입을 바탕으로 원하는 함수를 유추할 수 있다.
// 예시
void print (const char *a);
void print (const int *b, const int *c);
void print (const int a[], size_t size);
● main함수는 다중 정의가 불가능하다.
● 다중 정의한 함수 정의하기
○ 두 함수에서 반환 타입만 다르다면 오류이다.
● 두 매개변수 타입이 다른지 결정하기
// 각 쌍별로 같은 함수를 선언한다.
Record lookup(const Account &acct);
Record lookup(const Account&); // 매개변수 이름은 무시한다.
typedef Phone Telno;
Record lookup(const Phone&);
Record lookup(const Telno&); // Telno 와 Phone은 타입이 같다
● 다중 정의와 const 매개변수
○ 하위 const처럼 매개변수가 해당 타입의 const나 const가 아닌 버전에 대한 참조자나 포인터인지 여부를 바탕으로 다중 정의 가능하다.
● const_cast와 다중 정의
● 다중 정의한 함수 호출하기
○ 함수 일치는 다중 정의한 함수 집합에서 특정한 함수 호출을 특정 함수와 연관 짓는 과정이다.
6.4.1 다중 정의와 유효 범위
● 일반적으로 내부 유효 범위에 이름을 선언하면 그 이름은 외부 유효 범위에 선언한 이름을 가려 사용하지 못하게한다. 이름은 유효 범위를 가로질러 다중 정의하지 않는다.
6.5 전문적 용도를 위한 기능
● 유용한 세가지 함수 관련 기능인 기본 인자, 인라인, constexpr 함수를 다루고 디버깅할 떄 자주 사용하는 일부 기능을 다룬다.
6.5.1 기본인자
● 매개변수의 공통 값을 함수에 대한 기본 인자로 선언할 수 있다.
● 그 인자를 사용하거나 사용하지 않고 호출 할 수 있다.
● 매개변수 하나 이상에 대해 기본 값을 정의할 수 있지만 매개변수에 기본인자를 지정하면 그 매개변수 뒤에 오는 모든 매개변수에도 기본 인자를 지정 해야한다.
● 기본 인자를 사용해 함수 호출하기
○ 기본 인자를사용하려면 함수를 호출할 때 해당 인자를 생략하면 된다.
○ 호출에서 인자는 위치로 해석한다. 기본인자는 가장 후행에서 취급한다.
● 기본 인자 선언
○ 헤더에서 함수를 한 번 선언하는 것이 일반적인 관례이지만 함수를 여러번 재선언해도 괜찮다.
○ 해당 유효범위 안에서 각 매개변수에는 기본 값을 한 번만 지정할 수 있다.
● 기본 인자 초기화식
○ 지역 변수는 기본 인자로 사용할 수 없다. 이런 제약을 제외하고 해당 매개변수 타입으로 변환할 수 있는 모든 표현식은 기본 인자로 사용할 수 있다.
6.5.2 인라인과 constexpr 함수
● 6.3.2절에서는 두 string 매개변수 중 더 짧은 것에 대한 참조자를 반환하는 작은 함수를 만들었다. 그러한 작은 연산을 함수로 정의할 때 이점은 다음과 같다.
○ 동일한 표현식을 읽고 이해하는 것보다 shorterString에 대한 호출을 읽고 이해하는 것이 더 쉽다.
○ 함수를 사용하면 동일한 행동을 보장한다
○ 계산을 바꿔야 할 때, 동일한 표현식을 사용한 모든 위치를 찾아 변경하는 것보다 해당 함수를 변경하는 것이 더 쉽다.
○ 함수를 다른 응용 프로그램에서 다시 만들지 않고 재사용할 수 있다.
--> shorterString의 약점 : 함수 호출은 동일한 표현식을 평가하는 것보다 더 느린 경향이 있다.
● inline함수를 사용해 함수 호출에 추가 부담을 피한다.
○ inline으로 정의한 함수는 일반적으로 호출한 각위치에 '정렬해' 확장한다.
○ 일반적으로 inline 메커니즘에서 의도한 것은 자주 호출하는 작고 직선적인 함수에 대한 최적화이다.
cout << shorterString(s1, s2) << endl;
● constexpr 함수
○ 상수 표현식에 사용할 수 있는 함수이다.
○ 조건 - return 타입과 매개변수 타입이 상수 타입이어야 하고 함수 본체에는 return문이 정확히 하나만 있어야 한다.
○ 상수 표현식을 반환해야 하는 것은 아니다.
● inline과 constexpr 함수는 헤더 파일에 둔다.
6.5.3 오류 수정 지원 도구
● assert 전처리기 매크로
○ 전처리기 매크로는 어느정도 인라인 함수처럼 행동하는 전처리기 변수이다.
○ assert (표현식);
○ 0이면 에러 메세지를 출력하고 프로그램을 종료한다.
● NDEBUG 전처리기 변수
○ NDEBUG를 정의하면 assert에서는 아무것도 하지 않는다.
○ define 또는 명령행 옵션을 사용해 활성화 시킬 수 있다.
6.6 함수 일치
● 후보와 호출 가능 함수 결정하기
○ 후보 함수 : 함수 일치 첫 단계에서는 호출을 고려할 수 있는 다중 정의한 함수 집합이다. 함수 집합 == 후보 함수
○ 주어진 호출에서 사용한 인자로 호출할 수 있는 함수를 후보 함수 집합에서 선택한다. 이렇게 선택한 함수가 호출 가능 함수이다.
● 가장 일치하는 것(이 있다면) 찾기
○ 함수 기능 함수 중 어느 것이 해당 호출과 가장 일치하는지 결정한다.
● 여러 매개변수와 일치하는 함수
○ 이 경우 컴파일러가 모호함에 불평한다.
6.6.1 인자 타입 변환
● 가장 일치하는 것을 결정하기 위해 컴파일러에서는 각 인자를 해당 매개변수 타입을 변환하는 데 사용할 수 있는 변환에 순위를 매긴다. 변환 순위는 다음과 같다.
○ 정확한 일치
□ 인자와 매개변수 타입이 동일할 때,
□ 인자를 배열이나 함수 타입에서 해당 포인터 타입으로 변환할 떄
□ 상위 const를 인자에 추가하거나, 인자에서 버릴 때,
○ const 변환을 통한 일치
○ 승격을 통한 일치
○ 산술 또는 포인터 변환을 통한 일치
○ 클래스 타입 변환을 통한 일치
6.7 함수에 대한 포인터
● 함수 포인터는 단지 객체 대신 함수를 나타내는 포인터이다.
● 함수 타입은 반환 타입과 매개변수 타입으로 결정하며 함수 이름은 타입에 속하지 않는다.
● 함수 포인터 사용하기
○ 함수 이름을 값으로 사용하면 그 함수를 장동으로 포인터로 변환 한다. -> 이를 통해 호출할 수 있다.
○ nullptr이나 0을 넣어서 아무 함수도 가리키지 않는다는 것을 나타낼 수 있다.
● 다중 정의한 함수에 대한 포인터
○ 포인터 타입을 사용해 다중 정의한 함수 중 어느 것을 사용할지 결정한다.
● 함수 포인터 매개변수
○ 배열과 마찬가지로 함수 타입 매개변수는 정의할 수 없지만 함수에 대한 포인터를 매개변수로 할 수는 있다.
● 함수에 대한 포인터 반환하기
○ 배열과 마찬가지로 함수 타입을 반환 할 수는 없지만 함수 타입에 대한 포인터는 반환할 수 이다.
○ 타입 별칭을 사용하는 것이 가장 쉽다.
● 함수 포인터 타입에 auto나 decltype 사용하기
○ 어느 함수를 반환해야 하는지 알고 있다면 decltype을 사용해 함수 포인터 반환 타입을 간단히 만들 수 있다.
'C++ Primer' 카테고리의 다른 글
클래스 (0) | 2024.07.08 |
---|---|
문장 (0) | 2024.06.29 |
표현식 (0) | 2024.06.26 |
문자열, 벡터와 배열 (0) | 2024.06.26 |
변수와 기본 타입 (0) | 2024.06.25 |