ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C++ Primer CH6. Functions
    c++ 2024. 1. 21. 16:14

    6.1 Function Basics

    함수 밖에서 정의된 객체는 프로그램 실행이 끝날 때까지 존재한다.

    함수 안에서 정의된 지역 객체는 어떻게 정의되었는지에 따라 생명주기가 다르다.

     

    Automatic Objects

    :함수가 실행되는 동안만 살아있는 객체들

    인자(파라미터)도 여기에 포함됨.

    • 함수 시작시 파라미터가 저장소에 할당됨
    • 지역변수

    Local static Objects

    : 함수 내에 정의된 객체인데 함수가 끝났는데도 살아있는 static 객체

    프로그램이 끝나면 파괴됨

    default 초기화 값은 0이나 “”같은 것들이다.(value initialization)

    6.1.2 Function Declarations

    함수 선언은 헤더파일에 한다.

    이후, 함수 정의 파일에서 해당 헤더파일을 include한다.

    6.1.3 Separate Compilation

    하나의 프로그램을 여러개의 파일로 만든 후, 각각 컴파일해서 목적파일로 만든 후 링킹 과정을 거쳐 실행파일로 만든다.

    6.2 Argument Passing

    함수 파라미터가 참조자면 인자가 바인딩되지만, 그게 아니면 복사된다.

    참조자일 경우, passed by reference나 called by reference라고 불린다.

    파라미터 또한 또다른 참조자 of 오브젝트일 뿐이다.

     

    인자가 복사되면 passed by value, called by value라고 부른다.

     

    pointer paramenters

    void reset(int *ip)
    {
    	*ip = 0;
    	ip = 0;
    }
    

    위 코드에서 ip 포인터는 복사된 포인터이므로 메모리상에 따로 존재한다.

    이때, *ip = 0 하면 실제 object에 접근해서 값을 0으로 바꾸고,

    ip = 0 하면, 복사된 포인터가 nullptr이 되는 것일 뿐이지, 실제 포인터는 여전히 같은 오브젝트를 가리키고 있다.

     

    c에서는 파라미터로 포인터를 받아 외부 오브젝트 값 변경하는게 익숙하지만, c++에서는 참조자를 사용한다.

     

    pass by reference

    void reset(int &i)
    {
    	i = 0;
    }
    

    위 pointer parameters 코드와 같은 동작을 참조자를 사용해서 할 수 있다.

     

    복사를 피하기 위한 참조자 사용법

    IO 타입 클래스는 복사될 수 없다. 이럴때 사용하는게 참조자 파라미터다.

    string같이 복사비용이 클 때도 참조자를 사용한다.

    bool isShorter(const string &s1, const string &s2)
    {
    	return s1.size() < s2.size();
    }
    

    여기서 const &를 사용해서 내부에서 오브젝트 변경을 방지한다.

     

    추가정보를 반환하기 위한 참조자 사용법

    여러개의 값을 반환할 수 없으므로, 인자로 받은 참조자에다가 원하는 값을 넣어주면 여러개 값 반환하는 셈이다.

    첫번째로 c 문자가 나오는 인덱스와 c의 개수를 반환하는 함수

    string::size_type find_char(const string &s, char c, string::size_type &occurs)
    {
    	auto ret = s.size();
    	occurs = 0;
    	for (decltype(ret) i = 0; i != s.size(); ++i) {
    		if (s[i] == c) {
    			if (ret == s.size()) {
    				ret = i;
    			}
    			occurs++;
    	  }
    	}
    	return ret;
    }
    			
    

     

    const 파라미터와 인자

    int main()
    {
        const int ci = 42; // ci 자체가 바뀔 수 없다 -> top-level
        int i = ci;
        int *const p = &i; // p 자체가 바뀔 수 없다. -> top-level
        *p = 0;
        const int *p2 = &i; // p가 가리키는 주소에 있는 오브젝트의 값이 바뀔 수 없다. -> low-level
        p2 = &ci; // ok
        *p2 = 10; // error
    }
    

    top-level const rvalue는 할당시 무시된다.

     

    void fnc(const int i) {}
    void fnc(int i) {}
    // 두 함수 둘다 같은 타입의 인자를 받을 수 있다 -> 컴파일러는 어떤 함수
    // 를 사용할 지 모름 -> 컴파일 안되도록 처리.
    

     

    배열 참조자를 파라미터로 사용하기

    #include <iostream>
    using namespace std;
    
    void print(int (&arr)[10]) {
        for (auto &elem : arr)
            cout << elem << endl;
    }
    int main()
    {
        int arr[10] = {1, 2, 3, 4, 5};
        print(arr);
    }
    

    배열의 크기를 지정해줘야 하고, 인자배열의 크기와 일치해야 가능

    • 대부분의 경우에서 쓸모 없음 → ch16에서 크기와 상관없이 쓰는법 배움

    initializer_list → 가볍게 지나가기

    요소들이 모두 const로 됨

     

    Ellipsis 파라미터

     

    함수 리턴

     

    로컬 오브젝트의 참조자나 포인터 리턴 금지

    함수 끝나면 메모리에서 해제됨

     

    참조자 리턴은 Lvalue임

    참조자 리턴을 해도 돼?에 대한 코드

    char &get_val(string &str, string::size_type ix) {
    	return str[ix];
    }
    

    위의 경우 외부 string오브젝트의 참조자 str을 통해 해당 오브젝트의 ix번째 요소에 접근하는데, subscript는 lvalue이므로 char& 리턴 가능

    참조자는 lvalue임.

     

    그래서 이런것도 가능

    int main()
    {
    	string s("a value");
    	get_val(s, 0) = 'A';
    	cout << s << endl;
    	return 0;
    }
    

     

    리스트 반환도 가능

    vector<string> process()
    {
    	if (expected.empty())
    		return{}; // returnanemptyvector
    	else if (expected == actual)
    		return{"functionX","okay"};// returnlist-initializedvector
    	else
    		return {"functionX", expected, actual};
    }
    

     

    main에서의 리턴

    리턴 안해주면 암묵적으로 컴파일러가 0 리턴함

    • machine-independent 리턴 방법
    • -> cstdlib 헤더 include해서 EXIT_FAILURE, EXIT_SUCCESS 전처리 변수 사용

    배열 포인터 리턴 방법

    • decltype() 사용
    • trailing return type 사용
      • auto func(int i) → int(*)[10]

    6.4 Overloaded Functions

    이름은 같지만 다른 파라미터 리스트를 가지는 함수들은 overloaded되었다고 본다.

    파라미터 이름 다 빼고, typedef 써서 다른 타입인 것도 다 원래대로 돌렸을 때,

    파라미터가 top-level const면 들어오는 인자가 일반인지 const인지 구분할 수 없다(인자의 top const가 무시되기 때문에) → overload 불가

    파라미터가 low-level const면 인자의 low const 무시 안되서 구별 가능

    • 파라미터가 low-level이어야지 받을 수 있음.

    Overloading and Scope

    함수 선언을 함수 안에다가 하는 것

    6.5 Features for Specialized Uses

    Default Arguments

    • 함수 선언시에 값까지 집어 넣는 것

    6.5.2 Inline and constexpr Functions

    inline 함수는 함수 호출 오버헤드가 없다.

    • inline = in line(라인 안에 있음)
    cout << shorterString(s1, s2) << endl; // 인라인 전,
    cout << (s1.size() < s2.size() ? s1 : s2) << endl; // 후
    

    컴파일러가 inline 붙은걸 무시할 수도 있다.

    재귀함수는 컴파일러가 inline시켜주지 않는다. → 계속 라인에 추가될 거라서

    보통 간단하고 자주 호출되는 함수가 inline을 붙여서 사용함

    6.5.3 Aids for Debugging

    The assert Preprocessor Macro

    • 전처리 매크로이다.
      • inline 함수처럼 동작하는 전처리 변수
    • 형태: assert(expr)
    • cassert header
    • 전처리기 이름을 직접 사용하고 컴파일러에게 using 선언해줄 필요 없다
    • 매크로 이름은 유니크해야 한다

    NDEBUG Preprocessor Variable

    • 전처리 변수다
    • #define 으로 정의되어 있으면 assert가 무시됨
    • 정의 안되어있으면 assert는 정상적으로 런타임 체크함
    • assert 기능 on off 하게 해줌
    • CC -D NDEBUG main.C

    assert는 성능문제 야기할 수도 있으므로 검증할 때만 on 하자.

     

    다른 전처리 변수들

    • func
    • FILE
    • LINE
    • TIME
    • DATE

    6.6 Function Matching - 넘어가기

    함수 오버로딩이 많이 되어 있을 때, 타입 변환(conversion)이 될지말지 정하는 우선순위는 알기 쉽지 않다.

    6.7 Pointers to Functions

    함수를 가리키는 포인터

    bool (*pf)(const string &, const string &); // 초기화 안됨
    
    bool b1 = pf("hello", "goodbye");
    bool b2 = (pf)("hello", "goodbye");
    

     

     

    'c++' 카테고리의 다른 글

    C++ Primer CH8. The IO Library  (0) 2024.01.21
    C++ Primer CH7. Classes  (0) 2024.01.21
    C++ Primer CH4. Expressions  (0) 2024.01.21
    C++ Primer CH3. Strings, Vectors and Arrays  (0) 2024.01.21
    C++ Primer CH2. Variables and Basic Types  (0) 2024.01.21
Designed by Tistory.