6일차 - Cpp

JeongChaeJin·2021년 4월 19일
0

한컴아카데미

목록 보기
2/6

주석

  • //~ : 한 줄 주석

매개변수 디폴트값 (함수)

  • 함수의 디폴트 값은 선언 부분에만 표현하면 된다.
#include <iostream>
using namespace std;

int Adder(int num1=10, int num2=20);

int main(void)
{
    cout << Adder() << endl; // 30
    cout << Adder(3) << endl; // 23
    cout << Adder(3, 5) << endl; // 8

    return 0;
}

int Adder(int num1, int num2)
{
    return num1 + num2;
}
  • 디폴트 값의 선언이 함수의 선언부분에 위치해야 하는 이유 ?
    • 함수 본문에서 하면, 5, 6행을 어떻게 컴파일 하겠는가 ?

인라인 함수

#define SQUARE(x) ((x)*(x))

inline int SQUARE(x)
{
    return x * x;
}
  • 매크로 함수의 장점 : 일반 적인 함수에 비해 실행 속도의 이점
    • 함수간 이동과 같은 오버헤드를 제거한다.
  • 단점 : 정의하기 어려우며 복잡한 함수는 매크로 형태로 정의하는데 한계
  • 함수의 몸체부분이 함수호출 문장을 완전히 대체했을 때, 함수가 '인라인화 되었다'고 표현한다.
#include <iostream>
using namespace std;

/*
// int 로 됐을 때 다른 자료 형은 ? 매크로 함수는 다 되는데 ..? 이점이 없다
inline int SQUARE(int x)
{
    return x * x;
}
*/

template <typename T>
inline T SQUARE(T x)
{
    return x * x;
}

int main(void)
{
    cout << SQUARE(5) << endl;
    cout << SQUARE(12) << endl;

    return 0;
}
  • 템플릿을 사용하면 매크로 함수와 마찬가지로 자료형에 의존적이지 않은 함수가 완성된다.
  • ! Warning : 재귀 함수 또는 가상 함수, 함수 포인터를 사용하는 경우 인라인 함수가 될 수 없다.

namespace

#include <iostream>

namespace BestComp
{
    void SimpleFunc(void)
    {
        std::cout << "Best Comp Simple Func" << std::endl;
    }
}

namespace ProgComp
{
    void SimpleFunc(void)
    {
        std::cout << "Prog Comp Simple Func" << std::endl;
    }
}

int main(void)
{
    BestComp::SimpleFunc();
    ProgComp::SimpleFunc();
}
  • namespace는 특정 영역에 일므을 붙여주기 위한 문법적 요소 이다.
  • 201호 사는 철수, 202호 사는 철수 -> 아주 이해하기 좋은 예시

<또 다른 선언 방법>

#include <iostream>

namespace BestComp
{
    void SimpleFunc(void);
}

namespace ProgComp
{
    void SimpleFunc(void);
}

int main(void)
{
    BestComp::SimpleFunc();
    ProgComp::SimpleFunc();
}

void BestComp::SimpleFunc(void)
{
    std::cout << "Best Comp Simple Func" << std::endl;
}

void ProgComp::SimpleFunc(void)
{
    std::cout << "Best Comp Simple Func" << std::endl;
}
  • 선언과 구현의 분리한 표현

  • Name alias

#include <iostream>

namespace ABC = AAA::BBB::CC

ABC::num1 = 10;
ABc::num2 = 20;
  • 깊은 namespace도 편리하고, 폼나게 사용할 수 있다.

참조자 (Reference)

#include <iostream>
using namespace std;

int main(void)
{
    int num1 = 1020;
    int &num2 = num1; // 반드시 초기화 ! 안하면 Complie Error

    num2 = 3047;
    cout << "Val : " << num1 << endl;
    cout << "Ref : " << num2 << endl;

    cout << "Val : " << &num1 << endl;
    cout << "Ref : " << &num2 << endl;

    return 0;
}
  • 할당된 하나의 메모리 공간에 둘 이상의 이름을 부여한 것.
  • num1이라는 이름이 붙은 메모리 공간에 num2 라는 이름이 부여된 것과 같다.
  • 참조자 (&) 는 반드시 선언된 변수 중에서 Initialize 해줄 것 !
  • "이미 선언된 변수의 앞"에 & 연산자가 오면 "주소 값의 반환을 명령"
    "새로 선언된 변수 이름 앞"에 & 등장하면 "참조자의 선언"을 뜻하게 된다.
#include <iostream>
using namespace std;

int main(void)
{
    int num = 12;
    int *ptr = &num;
    int **dptr = &ptr;

    int &ref = num;
    int *(&pref) = ptr;
    int **(&dpref) = dptr;

    cout << ref << endl;
    cout << *pref << endl;
    cout << **dpref << endl;

    return 0;
}
  • 포인터 변수도 변수이므로 참조자의 선언이 가능하다.

  • const 참조자

    • const int &ref = 30;
    • 임시변수를 만들어 30을 저장하여 ref 가 이를 가리킨다.
    •  int Adder(const int &num1, const int &num2)
       {
           return num1 + num2;
       }
      • 위와 같은 함수를 간단히 호출할 수 있게 됐다.
    • const 로 상수화 시킨 변수를 참조하기 위해서는 참조자도 const 시켜야한다.
    • const int num = 20;
      const int &ref = num;

Call by value

  • 값을 인자로 전달하는 함수의 호출 방식
  • 함수 내부에서 외부에 선언된 변수에 접근이 불가능하다.

Call by reference

  • 주소 값을 인자로 전달하는 함수의 호출 방식

  • 외부 변수에 저장된 값을 변경하기 위해 필요하다.

    void SwapByRef(int * ptr1, int * ptr2)
    {
        int temp = *ptr1;
        *ptr1 = *ptr2;
        *ptr2 = temp;
    }
  • 위의 함수에서는 두 개 주소 값을 받아서 그 주소 값이 참조하는 영역에 저장된 값을 직접 변경하고 있다.

  • Call-by-Address ?

    • c++ 참조자 기반의 함수 호출과 구분을 짓기 위함.
    • 본래 C언어에서 말하는 Call-by-reference는
      "주소 값을 전달받아서, 함수 외부에 선언된 변수에 접근하는 형태의 함수 호출"을 말한다.
    • 이렇듯 주소 값이 전달되 었다는 사실보다 주소 값이 참조의 도구로 사용되었다는 사실이 중요한 것이다.
      • 주소 값을 이용한 Call-by-reference
      • 참조자를 이용한 Call-by-reference 이 두 가지로 저자는 나누고 있다.
    #include <iostream>
    using namespace std;
    
    void SwapByRef2(int &ref1, int& ref2)
    {
        int temp = ref1;
        ref1 = ref2;
        ref2 = temp;
    }
    
    int main(void)
    {
        int val1=10, val2=20;
    
        SwapByRef2(val1, val2);
        cout << "val1 : " << val1 << endl;
        cout << "val2 : " << val2 << endl;
    
        return 0;
    }
    • 참조자를 이용한 Call-by-reference 다.
    • Call-by-reference의 가장 큰 핵심인 함수 내에서 함수 외부에 선언된 변수에 접근할 수 있다는 것 ! 참조자를 이용해서도 쌉가능이다.
    • 함수를 호출함과 동시에 val1이 ref1이라는 또다른 이름으로, val2도 ref2라는 이름으로 된다. 그러니 교환하면 바뀐다.

const 와 Reference

  • 참조자를 사용하면 외부 변수의 값을 변경할 수 있다. 이는 단점이 될 수 있으므로 const를 사용하여 값의 변경을 예방할 수 있다.
  • 함수 내에서 참조자를 통한 값의 변경을 진행하지 않을 경우 참조자를 const로 선언해서 함수의 원형만 봐도 값의 변경이 이뤄지지 않음을 알 수 있게 한다 -> 이를 가급적 지키도록 하자.
void HappyFunc(const int &ref) 
// 함수 내에서 참조자 ref를 이용한 값의 변경은 하지 않겠다는 의미군 !\

반환형이 참조형이 라면 ?

  • 반환형에도 참조형 선언이 가능하다.
int& RefRetFuncOne(int &ref) { return ref } // 1.
int RefRetFuncOne(int &ref) { return ref } // 2.
    1. 은 반환형이 참조형이므로 ref라는 이름의 메모리의 참조이다.
    1. 는 반환형이 일반 int형 값이므로 ref 라는 이름의 메모리에 있는 값이다.
#include <iostream>
using namespace std;

int& RefRetFuncOne(int &ref)
{
    ref++;
    return ref;
}

int main(void)
{
    int num1 = 1;
    int &num2 = RefRetFuncOne(num1);
    //~ int num2 = RefRetFuncOne(num1); // 비교 

    num1++;
    num2++;
    cout << "num1 : " << num1 << endl;
    cout << "num2 : " << num2 << endl;

    return 0;
}
  • int &로 참조자 반환 함수를 받는 다면 ?
    • num1 , ref : 1, ref++에서 num1, ref : 2로 증가 후 num1, ref(함수 끝나고 사라짐), num2 : 2 -> num1++, num2++ 하니까 num1, num2 : 4가 된다.
  • int 로 참조자 반환 함수를 받는 다면 ?
    • num1, ref : 1, ref++에서 num1, ref : 2로 증가 후 num1 : 2, num2 : 2 (독립적) num1++ -> num1 : 3, num2++ -> num2 : 3
    • 이 차이를 알도록하자.
  • 한 가지 주의할 점은 참조형 반환 함수는 일반 타입의 변수에 담을 수 있고, 심지어 경고메세지만 나오고 컴파일에 성공한다.. (주의)
  • 하지만, 함수가 일반 타입 반환이라면 참조형 변수에 담지 못한다 !

참조형 반환 주의점

  • 지역 변수를 참조형으로 반환하지말자
    • 함수 내 증가한 지역 변수를 참조형 반환하여 외부 참조 변수에서 받는다면, 함수 호출 후 그 지역변수는 소멸할 것이므로 이런 등신짓은 하지말자.

new & delete

#include <iostream>
#include <string.h>
#include <stdlib.h>
using namespace std;

char * MakeStrAdr(int len)
{
    // char * str = (char*)malloc(sizeof(char)*len);
    char * str = new char[len];
    return str;
}

int main(void)
{
    char * str = MakeStrAdr(20);
    strcpy(str, "I am so happy~");
    cout << str << endl;
    // free(str);
    delete []str;
    return 0;
}
  • new 는 malloc 을 대신하는 키워드이며
  • delete 는 free 를 대신하는 키워드이다.
  • 불편했던점
    • 할당 대상의 정보를 바이트 크기 단위로 전달해야한다는 점
    • 반환형이 void 형 포인터이므로 적절한 형 변환을 거쳐야한다는 점
  • 개선점
    • 불편했던 점이 보다 할당 대상의 정보를 쉽게 명시할 수 있다.
profile
OnePunchLotto

0개의 댓글