C++/Syntax 2021. 3. 30. 00:01

람다 함수 (Lambda Function)

  • 익명 함수

  • 기본 구조 : -> return type {}

    • [] : 사용할 변수를 지정한다.

      • & : 해당 스코프 변수들을 레퍼런스로 사용한다. 특정 변수명을 &name과 같은 식으로 불러오는 것도 가능하다.

      • = : 해당 스코프 변수들의 값을 복사하여 사용하도록 한다.

      • this : 클래스에서 this를 사용할 때 쓴다.

    • () : 함수의 파라미터를 의미한다.

    • -> : 함수의 반환형을 명시할 수 있다. (생략 시 void)

    • {} : 함수의 body이다.


예제

  • 간단한 숫자, 문자열, 클래스를 출력하는 예제

    #include <iostream>
    #include <string>
    
    class A
    {
        std::string name_ = "default name";
    
    public:
        A() 
        {
            std::cout << "Default Constructor\n"; 
        }
    
        A(const std::string& name)
            : name_(name)
        {}
    
        A(const A& a) 
        {
            std::cout << "Copy Constructor\n"; 
            name_ = a.name_;
        }
    
        void    print() const
        {
            std::cout << name_ << std::endl;
        }
    };
    
    int         main()
    {
        using namespace std;
    
        [](const int& i) -> void { cout << "input : " << i << endl; }(1234);
    
        string name = "Jack Jack";
        [&]() {cout << name << endl; }();
    
        A a("Dash");
        [&]() { a.print(); }();
        [&a]() { a.print(); }();
        [=]() { a.print(); }();
    }
    
    /* stdout stderr
    input : 1234
    Jack Jack
    Dash
    Dash
    Copy Constructor
    Dash
    */
  • 알고리즘 라이브러리와 연계해서 사용하기 좋다.

  • bind() 함수를 이용하여 파라미터를 고정시킬 수도 있다.

    #include <iostream>
    #include <string>
    #include <vector>
    #include <algorithm>
    #include <functional>
    
    int         main()
    {
        using namespace std;
    
        vector<int> v({ 1, 2, 3, 4 });
    
        for_each(v.begin(), v.end(), [](int val) { cout << val << endl; });
    
        std::function<void(int)> func = [](int val) {cout << val << endl; };
        func(123);
    
        std::function<void()> func2 = std::bind(func, 456);
        func2();
    }
    
    /* stdout stderr
    1
    2
    3
    4
    123
    456
    */
  • 멤버 함수를 바인딩할 수도 있다.

    • placeholders는 입력해야 하는 인자의 개수를 의미한다.
    #include <iostream>
    #include <string>
    #include <vector>
    #include <algorithm>
    #include <functional>
    
    void        goodbye(const std::string& s)
    {
        std::cout << "Goodbye " << s << std::endl;
    }
    
    class Object
    {
    public:
        void hello(const std::string& s)
        {
            std::cout << "Hello " << s << std::endl;
        }
    };
    
    int         main()
    {
        using namespace std;
    
        Object instance;
    
        auto f = bind(&Object::hello, &instance, std::placeholders::_1);
    
        f("World!");
    
        auto f2 = bind(&goodbye, std::placeholders::_1);
    
        f2("World~");
    }
    
    /* stdout stderr
    Hello World!
    Goodbye World~
    */

'C++ > Syntax' 카테고리의 다른 글

C++ delete  (0) 2021.03.15
C++ 깊은 복사(Deep Copy)  (0) 2021.03.15
C++ 얕은 복사(Shallow Copy)  (0) 2021.03.15
C++ 함수 포인터 (Function Pointer)  (0) 2021.03.12
C++ 인라인 함수 (Inline Function)  (0) 2021.03.12
C++/Syntax 2021. 3. 15. 23:31

delete

  • 함수를 사용하지 못하도록 막는 역할을 하는 키워드이다.

    #include <iostream>
    #include <cassert>
    
    class Fraction
    {
      int    numerator_;
      int    denominator_;
    
    public:
      Fraction(char) = delete;
    
      explicit Fraction(int num = 0, int den = 1)
        : numerator_(num), denominator_(den)
      {
        std::cout << "Default constructor" << std::endl;
        assert(den != 0);
      }
    
      Fraction(const Fraction& fraction)
        : numerator_(fraction.numerator_), denominator_(fraction.denominator_)
      {
        std::cout << "Copy constructor called" << std::endl;
      }
    
      friend std::ostream& operator << (std::ostream& out, const Fraction& f)
      {
        out << f.numerator_ << " / " << f.denominator_;
        return (out);
      }
    };
    
    void    doSomething(Fraction frac)
    {
      std::cout << frac << std::endl;
    }
    
    int        main()
    {
      using namespace std;
    
      Fraction frac('c');  // 에러
    }
    • 빌드하면 아래와 같은 에러가 발생한다.

      error C2280: 'Fraction::Fraction(char)': attempting to reference a deleted function

'C++ > Syntax' 카테고리의 다른 글

C++ 람다 함수 (Lambda Function)  (0) 2021.03.30
C++ 깊은 복사(Deep Copy)  (0) 2021.03.15
C++ 얕은 복사(Shallow Copy)  (0) 2021.03.15
C++ 함수 포인터 (Function Pointer)  (0) 2021.03.12
C++ 인라인 함수 (Inline Function)  (0) 2021.03.12
C++/Syntax 2021. 3. 15. 23:30

깊은 복사(Deep Copy)

  • 하위 항목을 모두 복사하는 것

    #include <iostream>
    #include <cassert>
    
    class MyString
    {
      char*    data_ = nullptr;
      int        len_ = 0;
    
    public:
      MyString(const char* source = "")
      {
        assert(source);
    
        len_ = std::strlen(source) + 1;
        data_ = new char[len_];
    
        for (int i = 0; i < len_; ++i)
          data_[i] = source[i];
        data_[len_ - 1] = '\0';
      }
    
      ~MyString()
      {
        delete[] data_;
      }
    
      MyString(const MyString& source)
      {
        std::cout << "Copy constructor\n";
    
        len_ = source.len_;
    
        if (source.data_ != nullptr)
        {
          data_ = new char[len_];
    
          for (int i = 0; i < len_; ++i)
            data_[i] = source.data_[i];
        }
        else
          data_ = nullptr;
      }
    
      char*& getString() { return data_; }
    };
    
    int        main()
    {
      using namespace std;
    
      MyString    hello("Hello");
    
      cout << (int*)hello.getString() << endl;
      cout << hello.getString() << endl;
    
      {
        MyString    copy = hello;
    
        cout << (int*)copy.getString() << endl;
        cout << copy.getString() << endl;
      }
    
      cout << hello.getString() << endl;
    }
    • 얕은 복사에서 발생했던 문제가 발생하지 않는다.

'C++ > Syntax' 카테고리의 다른 글

C++ 람다 함수 (Lambda Function)  (0) 2021.03.30
C++ delete  (0) 2021.03.15
C++ 얕은 복사(Shallow Copy)  (0) 2021.03.15
C++ 함수 포인터 (Function Pointer)  (0) 2021.03.12
C++ 인라인 함수 (Inline Function)  (0) 2021.03.12
C++/Syntax 2021. 3. 15. 23:29

얕은 복사(Shallow Copy)

  • 얕은 복사는 허상 포인터(Dangling pointer) 문제를 일으킬 수 있다.

    #include <iostream>
    #include <cassert>
    
    class MyString
    {
      char*    data_ = nullptr;
      int        len_ = 0;
    
    public:
      MyString(const char* source = "")
      {
        assert(source);
    
        len_ = std::strlen(source) + 1;
        data_ = new char[len_];
    
        for (int i = 0; i < len_; ++i)
          data_[i] = source[i];
        data_[len_ - 1] = '\0';
      }
    
      ~MyString()
      {
        delete[] data_;
      }
    
      char*& getString() { return data_; }
    };
    
    int        main()
    {
      using namespace std;
    
      MyString    hello("Hello");
    
      cout << (int*)hello.getString() << endl;
      cout << hello.getString() << endl;
    
      {
        MyString    copy = hello;
    
        cout << (int*)copy.getString() << endl;
        cout << copy.getString() << endl;
      }
    
      cout << hello.getString() << endl;
    }
    
    /* stdout
    00F2DD50
    Hello
    00F2DD50
    Hello
    ▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌ä↕O±▌↨
    */
    
    /* Runtime Error
    Debug Assertion Failed!
    
    Expression : _CrtlsValidHeapPointer(block)
    */
    • copy 인스턴스에서 소멸자를 호출할 때 해제된 메모리를 이후에 다시 사용하려고 하므로 런타임 에러가 발생한다.

'C++ > Syntax' 카테고리의 다른 글

C++ delete  (0) 2021.03.15
C++ 깊은 복사(Deep Copy)  (0) 2021.03.15
C++ 함수 포인터 (Function Pointer)  (0) 2021.03.12
C++ 인라인 함수 (Inline Function)  (0) 2021.03.12
C++ 레퍼런스 (Reference, 참조)  (0) 2021.03.12
C++/Syntax 2021. 3. 12. 17:30

함수 포인터 (Function Pointer)

기본 예제

배열에서 짝수, 홀수를 각각 출력하는 예제

  • 간단한 버전

      #include <iostream>
      #include <array>
    
      using namespace std;
    
      void    printNumbers(const array<int, 10>& arr, \
          bool print_even)
      {
          for (auto e : arr)
              if ((print_even && e % 2 == 0) || \
                  (!print_even && e % 2 == 1))
                  cout << e;
          cout << endl;
      }
    
      int     main()
      {
          array<int, 10> arr{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    
          printNumbers(arr, true);
          printNumbers(arr, false);
      }
    
      /* stdout stderr
      02468
      13579
      */

  • 함수를 나누고 함수 포인터로 전달

    #include <iostream>
    #include <array>
    
    using namespace std;
    
    bool    isEven(const int& number)
    {
      if (number % 2 == 0)
        return true;
      else
        return false;
    }
    
    bool    isOdd(const int& number)
    {
      if (number % 2 == 1)
        return true;
      else
        return false;
    }
    
    void    printNumbers(const array<int, 10>& arr, \
      bool (*check_fcn)(const int&) = isEven)
    {
      for (auto e : arr)
        if (check_fcn(e))
          cout << e;
      cout << endl;
    }
    
    int     main()
    {
      array<int, 10> arr{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    
      printNumbers(arr);
      printNumbers(arr, isOdd);
    }
    
    /* stdout stderr
    02468
    13579
    */

  • typedef로 정리

    #include <iostream>
    #include <array>
    
    using namespace std;
    
    bool    isEven(const int& number)
    {
      if (number % 2 == 0)
        return true;
      else
        return false;
    }
    
    bool    isOdd(const int& number)
    {
      if (number % 2 == 1)
        return true;
      else
        return false;
    }
    
    typedef bool (*check_fcn_t)(const int&);
    //using check_fcn_t = bool (*)(const int&);
    
    void    printNumbers(const array<int, 10>& arr, \
      check_fcn_t check_fcn = isEven)
    {
      for (auto e : arr)
        if (check_fcn(e))
          cout << e;
      cout << endl;
    }
    
    int     main()
    {
      array<int, 10> arr{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    
      printNumbers(arr);
      printNumbers(arr, isOdd);
    }
    
    /* stdout stderr
    02468
    13579
    */

  • C++11의 <functional> 라이브러리 사용

    #include <iostream>
    #include <array>
    #include <functional>
    
    using namespace std;
    
    bool    isEven(const int& number)
    {
      if (number % 2 == 0)
        return true;
      else
        return false;
    }
    
    bool    isOdd(const int& number)
    {
      if (number % 2 == 1)
        return true;
      else
        return false;
    }
    
    void    printNumbers(const array<int, 10>& arr, \
      std::function<bool(const int&)> check_fcn)
    {
      for (auto e : arr)
        if (check_fcn(e))
          cout << e;
      cout << endl;
    }
    
    int     main()
    {
      array<int, 10> arr{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    
      std::function<bool(const int&)> fcnptr = isEven;
    
      printNumbers(arr, fcnptr);
      fcnptr = isOdd;
      printNumbers(arr, fcnptr);
    }
    
    /* stdout stderr
    02468
    13579
    */

'C++ > Syntax' 카테고리의 다른 글

C++ 깊은 복사(Deep Copy)  (0) 2021.03.15
C++ 얕은 복사(Shallow Copy)  (0) 2021.03.15
C++ 인라인 함수 (Inline Function)  (0) 2021.03.12
C++ 레퍼런스 (Reference, 참조)  (0) 2021.03.12
C++ Stack Size  (0) 2021.03.11
C++/Syntax 2021. 3. 12. 17:27

인라인 함수 (Inline Function)

  • 함수 반환 값 정의 앞에 inline 키워드를 붙여주면 된다.

  • 컴파일러가 인라인으로 넣을지 결정한다.

    • inline 키워드를 넣지 않아도 인라인 함수로 작동시킬 때도 있고, 키워드를 넣어도 인라인 함수로 작동하지 않는 경우가 있다.

'C++ > Syntax' 카테고리의 다른 글

C++ 얕은 복사(Shallow Copy)  (0) 2021.03.15
C++ 함수 포인터 (Function Pointer)  (0) 2021.03.12
C++ 레퍼런스 (Reference, 참조)  (0) 2021.03.12
C++ Stack Size  (0) 2021.03.11
C++ 문자열 (string)  (0) 2021.03.11
C++/Syntax 2021. 3. 12. 17:19

레퍼런스 (Reference, 참조)

함수의 인자로 const int & 형태를 자주 사용하는 이유

  • 레퍼런스 : 불필요한 복사가 발생하지 않아서 성능 상 이점을 가진다.

  • const : rvalue도 인자로 넘길 수 있어서 확장성이 좋아진다.


Call by Reference

  • 포인터를 레퍼런스로 전달하는 방법

    #include <iostream>
    
    void    foo(int*& ptr)
    {
        std::cout << ptr << ' ' << &ptr << std::endl;
    }
    
    int     main()
    {
        using namespace std;
    
        int        x = 5;
        int*    ptr = &x;
    
        cout << ptr << ' ' << &ptr << endl;
        foo(ptr);
    }

'C++ > Syntax' 카테고리의 다른 글

C++ 함수 포인터 (Function Pointer)  (0) 2021.03.12
C++ 인라인 함수 (Inline Function)  (0) 2021.03.12
C++ Stack Size  (0) 2021.03.11
C++ 문자열 (string)  (0) 2021.03.11
C++ nullptr_t  (0) 2021.03.11
C++/Syntax 2021. 3. 11. 16:55

Stack Size

  • VS에서는 아래와 같은 경우 경고를 띄운다.

    int     main()
    {
      using namespace std;
    
      int arr[10000];
      (void)arr;
    }
  • 경고 내용

    Warning C6262    Function uses '40000' bytes of stack:  exceeds /analyze:stacksize '16384'.  Consider moving some data to heap.
  • 찾아보니 VS의 기본 스택 프레임 사이즈가 16KB로 설정되어있었고, 변경 가능하다.

  • 스택 사이즈를 제한하는 이유는 쓰레드 개수의 확보를 위해서, 또 스택 오버플로우를 방지하기 위해서라고 한다.

    • OS에 따라 스택 오버플로우 발생 시 자동으로 스택 사이즈를 늘리는 방식도 있다고 한다.

    • 임베디드 시스템에서는 보통 그런 기능이 없기 때문에 주의해야 한다.

'C++ > Syntax' 카테고리의 다른 글

C++ 인라인 함수 (Inline Function)  (0) 2021.03.12
C++ 레퍼런스 (Reference, 참조)  (0) 2021.03.12
C++ 문자열 (string)  (0) 2021.03.11
C++ nullptr_t  (0) 2021.03.11
C++ 배열 (Array)  (0) 2021.03.11
C++/Syntax 2021. 3. 11. 16:54

문자열 (string)

text segment

  #include <iostream>

  int     main()
  {
    using namespace std;

    const char* name = "Jack jack";
    const char* name2 = "Jack jack";

    cout << (uintptr_t)name << endl;
    cout << (uintptr_t)name2 << endl;
  }
  • 두 출력 결과는 같다.

  • 메모리 영역 중 text segment(=code segment, text) 에 저장된 데이터의 주소를 불러오는 것이다.

  • 위키피디아


연산자 오버로딩

  • std::cout << (char *)

    • char * 형태의 주소가 오면 문자열로 인식한다.

    • 즉 null character('\0')가 등장할 때까지 출력한다.

    • 다음과 같은 예제에서는 주소가 나오지 않고 Q儆儆m8? 이런식으로 나온다.

      #include <iostream>
      
      int     main()
      {
        using namespace std;
      
        char c = 'Q';
        cout << &c << endl;
      }

'C++ > Syntax' 카테고리의 다른 글

C++ 레퍼런스 (Reference, 참조)  (0) 2021.03.12
C++ Stack Size  (0) 2021.03.11
C++ nullptr_t  (0) 2021.03.11
C++ 배열 (Array)  (0) 2021.03.11
C++ 반복문  (0) 2021.03.11
C++/Syntax 2021. 3. 11. 16:52

nullptr_t

  • C++11

  • C언어에서 사용되지는 않으나, <cstddef> 라이브러리에 존재하는 자료형이다.

    #include <cstddef>
    #include <iostream>
    
    void f(int*)
    {
        std::cout << "Pointer to integer overload\n";
    }
    
    void f(double*)
    {
        std::cout << "Pointer to double overload\n";
    }
    
    void f(std::nullptr_t)
    {
        std::cout << "null pointer overload\n";
    }
    
    int main()
    {
        int* pi {}; double* pd {};
    
        f(pi);
        f(pd);
        f(nullptr); // would be ambiguous without void f(nullptr_t)
        // f(0);    // ambiguous call: all three functions are candidates
        // f(NULL); // ambiguous if NULL is an integral null pointer constant 
                    // (as is the case in most implementations)
    }

--- 참고

'C++ > Syntax' 카테고리의 다른 글

C++ Stack Size  (0) 2021.03.11
C++ 문자열 (string)  (0) 2021.03.11
C++ 배열 (Array)  (0) 2021.03.11
C++ 반복문  (0) 2021.03.11
C++ switch  (0) 2021.03.11
C++/Syntax 2021. 3. 11. 16:48

배열 (Array)

상수 포인터, 포인터 상수는 무슨 의미인지는 알겠는데 사용하면 더 헷갈린다...


변수란 뭘까

#include <iostream>

int     main()
{
  using namespace std;

  int a = 1;

  cout << &a << endl;
}

/* stdout
00FAF990
*/
  • int a = 1을 정의하면 스택에 sizeof(int) 만큼 공간이 확보된다.

  • 여기서의 변수명 a는 단지 프로그래머가 메모리 공간에 접근할 때 필요한 것이다.

  • a라는 변수는 컴파일러가 기계어로 번역할 때 메모리 주소로 변환된다.

  • 즉 사람의 입장에서 메모리에 편하게 접근하려고 만든게 변수이고, 컴파일러가 처리하는 것이다.


배열 예제

#include <iostream>

int     main()
{
  using namespace std;

  int        arr[10]{ 0, 1, 2, 3, 4, 5, };

  cout << typeid(arr).name() << endl;     // int [10]
  cout << typeid(&arr).name() << endl;    // int (*)[10]
  cout << typeid(&arr[0]).name() << endl; // int *

  cout << sizeof(arr) << endl;            // 40
  cout << sizeof(&arr) << endl;           // 4
  cout << sizeof(&arr[0]) << endl;        // 4

  // 이 3개는 같은 주소를 가리킴
  cout << arr << endl;
  cout << &arr << endl;
  cout << &arr[0] << endl;
  // 배열 두 번째 요소의 주소
  cout << &arr[1] << endl;
  // 아래는 배열 전체의 다음 주소 (이렇게 사용하면 안됨)
  cout << (&arr)[1] << endl;
}

/* stdout
int [10]
int (*)[10]
int *
40
4
4
0118F840
0118F840
0118F840
0118F844
0118F868
*/
  • int arr[10]을 정의하면 스택에 sizeof(int) * 10 만큼 공간이 확보된다.

  • 배열의 이름인 arr은 사람이 편하게 접근할 수 있도록 컴파일러가 처리하는 것이다.

  • typeid().name()

    • 자료형을 표현

    • arr : int [10]

      • 배열 자료형이라고 생각하면 될 듯

      • 배열 자료형이라는 것은 들어본 적이 없으나, 내부적으로 이렇게 처리한다고 생각하는게 맞는 것 같다.

    • &arr : int (*)[10]

      • int [10] 의 배열을 가리키는 포인터

      • 이 주소에 1을 더하면 배열의 크기만큼 주소가 이동된다.

    • &arr[0] : int *, arr[0]를 찾아가서 주소를 찍은 것이므로 일반적인 포인터와 같음

  • javascript 의 경우 배열을 선언한 뒤 typeof 로 살펴보면 Object 라고 뜨는 것을 볼 수 있다.


에러 예제

int     main()
{
  int* p;
  int(*pa)[3];
  int arr[2][3] = { 3, };

  p = arr;        // cannot convert from 'int [2][3]' to 'int *'
  p = (int *)arr; // 이건 가능
  p = &arr;       // cannot convert from 'int (*)[2][3]' to 'int *'
  pa = arr;       // 가능
  pa = &arr;      // cannot convert from 'int (*)[2][3]' to 'int (*)[3]'

  int(*ppa)[2][3];

  ppa = arr;      // cannot convert from 'int [2][3]' to 'int (*)[2][3]'
  ppa = &arr;     // 가능

  int arr2[2];
  pa = arr2;      // cannot convert from 'int [2]' to 'int (*)[3]'

  int arr3[2][3][4];
  int*** ppp;
  ppp = arr3;     // cannot convert from 'int [2][3][4]' to 'int ***'
}
  • 7번째 줄 (p = arr;)
    • int [2][3] 이라는 2차원 배열 자료형을 int * 에 대입하려해서 오류 발생
  • 8번째 줄
    • 직접 형변환을 한 뒤 역참조를 해보면 값이 제대로 나오는 것을 확인할 수 있다.
    • int [2][3] 도 주소를 나타내지만, 묵시적 형변환이 작동하지 않는 경우임을 알 수 있다.
      • 비슷한 예로 int * 변수에 double * 변수 값을 대입하면, cannot convert from 'double *' to 'int *' 에러가 발생하는 것을 볼 수 있다.
  • 9번째 줄
    • &arrint (*)[2][3] 타입이라는 것은 int [2][3]의 배열을 가리키는 포인터임을 의미한다.
  • 10번째 줄
    • arrint [2][3] 타입이고, 묵시적 형변환을 통해 int (*)[3]으로 변환되었음을 볼 수 있다.

배열을 레퍼런스로 전달하는 예제

#include <iostream>

void    printElements(const int(&arr)[5])
{
  std::cout << arr << std::endl;
  for (int i = 0; i < 5; ++i)
    std::cout << arr[i] << " ";
  std::cout << std::endl;
}

int     main()
{
  using namespace std;

  int arr[5] = { 1, 2, 3, 4, 5 };

  std::cout << arr << std::endl;
  printElements(arr);    
}

/* stdout
00CFFC08
00CFFC08
1 2 3 4 5
*/

참고


std::array 컨테이너

'C++ > Syntax' 카테고리의 다른 글

C++ 문자열 (string)  (0) 2021.03.11
C++ nullptr_t  (0) 2021.03.11
C++ 반복문  (0) 2021.03.11
C++ switch  (0) 2021.03.11
C++ 제어 흐름 (Control Flow)  (0) 2021.03.11
C++/Syntax 2021. 3. 11. 16:29

반복문 (Loop)

  • unsigned int 의 연산이 일반적으로 int 보다 빠르다.

    • 음수를 사용하는게 아니라면 unsigned int 자료형을 사용하는게 성능면에서 더 좋다.

'C++ > Syntax' 카테고리의 다른 글

C++ nullptr_t  (0) 2021.03.11
C++ 배열 (Array)  (0) 2021.03.11
C++ switch  (0) 2021.03.11
C++ 제어 흐름 (Control Flow)  (0) 2021.03.11
C++ Type Alias  (0) 2021.03.11
C++/Syntax 2021. 3. 11. 16:28

switch

  • switch문 내부에서 중괄호 없이 변수 선언은 가능하지만, 초기화는 안된다.

    • 중괄호가 없으면 변수의 scope는 switch문의 중괄호까지인데, 서로 다른 case에서 변수에 접근은 가능하지만 초기화가 안된 상태로 접근할 수 있기 때문이다.

    • 특별한 상황이 아니라면 이렇게 사용하는 것은 별로 추천하는 방법은 아니다.

    • MS 에러 코드

    #include <iostream>
    
    int main()
    {
        using namespace std;
    
        int x;
        cin >> x;
    
        switch (x)
        {
            int a;          // 이건 가능
            int b = 5;      // Error: initialization of 'b' is skipped by 'case' label
    
        case 0:
            int c;          // 이것도 가능
            int d = 5;      // 이건 안됨
            {               // 굳이 써야되면 이런 식으로 작성
              int e = 5;
              cout << e << endl;
            }
            a = 1;
            cout << a << endl;
        }
    }

'C++ > Syntax' 카테고리의 다른 글

C++ 배열 (Array)  (0) 2021.03.11
C++ 반복문  (0) 2021.03.11
C++ 제어 흐름 (Control Flow)  (0) 2021.03.11
C++ Type Alias  (0) 2021.03.11
C++ 열거형 (Enumerate Type)  (0) 2021.03.10
C++/Syntax 2021. 3. 11. 16:27

제어 흐름 (Control Flow)

중단(Halt)

  • return, exit

점프(Jump)

  • goto, break, continue

조건 분기(Conditional branches)

  • if, switch

반복(Loop)

  • while, do while, for

예외 처리(Exception)

  • try, catch, throw

'C++ > Syntax' 카테고리의 다른 글

C++ 반복문  (0) 2021.03.11
C++ switch  (0) 2021.03.11
C++ Type Alias  (0) 2021.03.11
C++ 열거형 (Enumerate Type)  (0) 2021.03.10
C++ 형변환 (Type Conversion)  (0) 2021.03.10
C++/Syntax 2021. 3. 11. 16:25

Type Alias

  • 자료형, 클래스 등을 다른 이름으로도 사용할 수 있게 해준다.

typedef

  typedef vector<pair<string, int>> pairlist_t;

using

  using pairlist_t = vector<pair<string, int>>;

'C++ > Syntax' 카테고리의 다른 글

C++ switch  (0) 2021.03.11
C++ 제어 흐름 (Control Flow)  (0) 2021.03.11
C++ 열거형 (Enumerate Type)  (0) 2021.03.10
C++ 형변환 (Type Conversion)  (0) 2021.03.10
C++ 자료형 추론 (auto, decltype)  (0) 2021.03.10
C++/Syntax 2021. 3. 10. 21:12

열거형 (Enumerate Type)

unscoped enum

  • C++98

  • 범위에 제한이 없어 모든 namespace에 선언된다.

    • 따라서 이름이 중복되면 안된다.
  • 정수 타입으로 묵시적 형변환된다.

  • enum 변수에 숫자를 넣으려면 캐스팅을 해야 한다.

    #include <iostream>
    
    enum Color
    {
        COLOR_BLACK,
        COLOR_RED,
        COLOR_BLUE,
        COLOR_GREEN
    };
    
    int main()
    {
        using namespace std;
    
        int color_id = COLOR_RED;
        Color my_color1 = static_cast<Color>(color_id);
        Color my_color2 = static_cast<Color>(3);
        if (my_color1 < 2)
            cout << my_color1 << ' ' << my_color2 << endl;
    }
  • VS에서 작성 시 unscoped enum을 사용했다는 경고가 발생한다.


scoped enum

  • enum class

  • C++11

  • 영역을 제한해준다.

  • 묵시적 형변환을 허용하지 않는다.

  • 전방 선언 가능

    • C++98에서 unscoped enum은 전방선언 불가

      • 컴파일러가 숨겨진 타입을 모르기 때문이다.
    #include <iostream>
    
    enum class Color
    {
        COLOR_BLACK,
        COLOR_RED,
        COLOR_BLUE,
        COLOR_GREEN
    };
    
    int main()
    {
        using namespace std;
    
        int color_id = static_cast<int>(Color::COLOR_RED);
        Color my_color1 = static_cast<Color>(color_id);
        Color my_color2 = static_cast<Color>(3);
        if (my_color1 < static_cast<Color>(2))
            cout << static_cast<int>(my_color1) << ' ' \
                << static_cast<int>(my_color2) << endl;
    }
  • 숨겨진 타입(underlying type)이 기본적으로 int 타입이다.

    • 직접 타입을 명시할 수 있다.

      enum class Color : uint32_t
      {
          COLOR_BLACK,
          COLOR_RED,
          COLOR_BLUE,
          COLOR_GREEN
      };
    • C++11에서 unscoped enum을 전방선언하는 방법

      • 숨겨진 타입을 직접 명시하면, 컴파일러가 enum의 숨겨진 타입을 알 수 있으므로 가능하다.

        enum Color : std::uint32_t;

'C++ > Syntax' 카테고리의 다른 글

C++ 제어 흐름 (Control Flow)  (0) 2021.03.11
C++ Type Alias  (0) 2021.03.11
C++ 형변환 (Type Conversion)  (0) 2021.03.10
C++ 자료형 추론 (auto, decltype)  (0) 2021.03.10
C++ using  (0) 2021.03.10
C++/Syntax 2021. 3. 10. 21:10

형변환 (Type Conversion)

변환 방식

  • C Style

    (char)65
  • C++ Style

    char(65)
    static_cast<char>(65)

Numeric Promotion

  • 크기가 작은 자료형에서 큰 자료형으로 형 변환하는 것

    ex) float -> double


절삭(truncation)

  int    i = 30000;  // 0b111010100110000
  short s = i;    // 1바이트만 저장되어 0b00110000, 즉 48

  double d = 0.123456789;
  float f = d;  // 정밀도가 부족해서 다른 값이 저장됨

묵시적 형변환(implicit type conversion)에서의 자료형 별 우선 순위

  long double (highest)
  double
  float
  unsigned long long
  long long
  unsigned long
  long
  unsigned int
  int (lowest)
  char
  bool

'C++ > Syntax' 카테고리의 다른 글

C++ Type Alias  (0) 2021.03.11
C++ 열거형 (Enumerate Type)  (0) 2021.03.10
C++ 자료형 추론 (auto, decltype)  (0) 2021.03.10
C++ using  (0) 2021.03.10
C++ extern  (0) 2021.03.10
C++/Syntax 2021. 3. 10. 21:09

자료형 추론 (auto, decltype)

  • C++11

  • 자료형을 기본 타입 형태로 추론한다.

    • 자료형을 명시하기 복잡하거나 귀찮을 때 사용한다.

    • 가독성이 좋다.

  • 함수의 반환 값에도 적용할 수 있다.

    • Trailing Return Type(후행 반환 형식)

      • auto 키워드로 정의된 함수 반환 값의 자료형을 명시하는 것

      • 함수 앞에 자료형을 쓰는 것보다 가독성이 좋고, 인덴팅을 맞추기도 편하다.

      auto add(int x, int y)       -> int;
      auto add(double x, double y) -> double;

예제

iterator 대체

  • 개인적으로 이 때 제일 많이 사용하는 것 같다.

    #include <iostream>
    #include <vector>
    
    int        main()
    {
        using namespace std;
    
        vector<int> vec({ 1, 2, 3 });
    
        for (vector<int>::iterator itr = vec.begin(); itr != vec.end(); ++itr)
            cout << *itr;
        cout << endl;
        for (auto itr = vec.begin(); itr != vec.end(); ++itr)
            cout << *itr;
        cout << endl;
        for (const auto & itr : vec)
            cout << itr;
        cout << endl;
    }
    
    /* stdout
    123
    123
    123
    */

기본 자료형 대입

  • 작동 원리는 템플릿 타입 추론처럼 컴파일할 때 값을 복사해서 인스턴스화한다고 생각하면 될 것 같다.

    int        main()
    {
        using namespace std;
    
        int             x = int();
        auto            auto_x = x;         // int
        const int&      crx = x;
        auto            auto_crx1 = crx;    // int
        const auto&     auto_crx2 = crx;    // const int &
        volatile int    vx = 1024;
        auto            avx = vx;           // int
        volatile auto   vavx = vx;          // volatile int
    }
    • auto_crx1의 경우 원본인 crxconst int& 자료형이므로 그 값을 R-value로 받아서 복사하는 것이다.

    • auto_crx2crxconst int& 자료형이므로 값이 int형이고, 따라서 const int&와 같아진다.

    • avx의 경우에도 vxR-value처럼 값만 복사하여 int 자료형이 되는 것이다.


템플릿 타입 추론 (Template Type Deduction)

  • auto 키워드와 비슷하게 자료형을 추론해낸다.

    template <typename T>
    void    func(T arg)
    {}
    
    int        main()
    {
        const int& crx = 123;
    
        func(crx);              // void func<int>(int arg)
    }
    template <typename T>
    void    func(const T& arg)
    {}
    
    int        main()
    {
        const int& crx = 123;
    
        func(crx);              // void func<int>(const int& arg)
    }

레퍼런스

  • auto &의 형태로 사용하면 L-value 레퍼런스를 참조하는 것이기 때문에 const 등의 속성이 유지된다.

    int        main()
    {
        volatile int   c = 0;
        auto&       rc = c;     // const int &
    }

L-value, R-value References

  • auto &&의 형태로 사용하면 상황에 따라 달라진다.

    int        main()
    {
      int        i = 42;
      auto&& ri_1 = i;        // int &
      auto&& ri_2 = 42;        // int &&
    }

const 포인터

  • 포인터의 const * 속성은 유지된다.

    • 원본은 건드리면 안된다는 속성을 유지해야 하기 때문이다.
    int        main()
    {
      int            x = 42;
      const int*    p1 = &x;
      auto        p2 = p1;    // const int*
    }

자료형이 정해지지 않은 변수 사용 예제

  • 템플릿에 어떤 자료형이 들어오는가에 따라 변수의 자료형이 달라지는데, auto로 간단하게 처리할 수 있다.

    template <typename T, typename S>
    void    func(T lhs, S rhs)
    {
      auto    prod1 = lhs * rhs;
    
      typedef decltype(lhs* rhs)    product_type;
    
      product_type prod2 = lhs * rhs;
    
      decltype(lhs * rhs) prod3 = lhs * rhs;
    }
    • product_typeauto 키워드가 등장하기 이전에 사용했던 방식이다.

    • decltype

      • 일부 컴파일러에서 관습적으로 사용하던 typeof를 공식적으로 내놓은 것이다.

자료형이 정해지지 않은 반환 값 정의 예제

  • autodecltype을 같이 사용하여 쉽게 해결할 수 있다.

    template <typename T, typename S>
    auto    func(T lhs, S rhs) -> decltype(lhs* rhs)
    {
      return lhs * rhs;
    }

auto와 decltype의 차이

  • decltype은 변수가 선언될 때의 자료형을 가져온다.

    • decltype 내부에 괄호가 감싸져있으면 L-value 레퍼런스로 가져온다.
    class S
    {
    public:
      int    x_;
    
      S()
      {
        x_ = 42;
      }
    };
    
    int        main()
    {
      int            x;
      const int    cx = 42;
      const int&    crx = x;
      const S*    p = new S();
    
      auto a = x;        // int
      auto b = cx;        // int
      auto c = crx;        // int
      auto d = p;        // const S*
      auto e = p->x_;    // int
    
      typedef decltype(x)            x_type;        // int
      typedef decltype(cx)        cx_type;        // const int
      typedef decltype(crx)        crx_type;        // const int&
      typedef decltype(p->x_)        x__type;    // int
    
      typedef decltype((x))        x_with_parens_type;            // int &
      typedef decltype((cx))        cx_with_parens_type;    // const int&
      typedef decltype((crx))        crx_with_parens_type;    // const int&
      typedef decltype((p->x_))    x__with_parens_type;        // const int&
    }
  • std::vector의 경우 [] 연산자 오버로딩이 L-value 레퍼런스를 반환하기 때문에 decltype이 레퍼런스로 잡힌다.

    #include <iostream>
    #include <vector>
    
    class S
    {
    public:
      int    x_;
    
      S()
      {
        x_ = 42;
      }
    };
    
    const S        foo()
    {
      return S();
    }
    
    const int&    foobar()
    {
      return 123;
    }
    
    int            main()
    {
      using namespace std;
    
      vector<int> vec = { 42, 43 };
    
      auto a = foo();                                // S
      typedef decltype(foo()) foo_type;                // const S
    
      auto b = foobar();                            // int
      typedef decltype(foobar()) foobar_type;        // const int&
    
      auto itr = vec.begin();                        // vector<int>::iterator
      typedef decltype(vec.begin()) iterator_type;    // vector<int>::iterator
    
      auto first_element = vec[0];                    // int
      decltype(vec[1]) second_element = vec[1];        // int&
    }
  • decltype에서 삼항연산자 등으로 값을 그대로 반환하게 될 경우 L-value 레퍼런스로 추론된다.

    int            main()
    {
      int x = 0;
      int y = 0;
      const int cx = 42;
      const int cy = 43;
      double d1 = 3.14;
      double d2 = 2.72;
    
      typedef decltype(x * y) prod_xy_type;    // int
      auto a = x * y;                            // int            
    
      typedef decltype(cx * cy) prod_xy_type;    // int
      auto a = cx * cy;                        // int
    
      typedef decltype(d1 < d2 ? d1 : d2) cond_type;    // double&
      auto c = d1 < d2 ? d1 : d2;                        // double
    
      typedef decltype(x < d2 ? x : d2) cond_type_mixed;    // double
      auto d = x < d2 ? x : d2;                            // double
    }
  • 위의 L-value 레퍼런스로 반환하는 특성때문에, std::remove_reference 구조체를 이용해야 할 때도 있다.

    • 반환 값이 L-value 레퍼런스일 때도 있고 R-value일 때도 있으면 헷갈리기 때문이다.
    #include <iostream>
    
    template <typename T, typename S>
    auto fpmin_wrong(T x, S y) -> decltype(x < y ? x : y)
    {
      return x < y ? x : y;
    }
    
    template <typename T, typename S>
    auto fpmin(T x, S y) ->
    typename std::remove_reference<decltype(x < y ? x : y)>::type
    {
      return x < y ? x : y;
    }
    
    int            main()
    {
      int        i = 42;
      double    d = 45.1;
      //auto    a = std::min(i, d); // 에러
      auto    a = std::min(static_cast<double>(i), d);
    
      int&    j = i;
    
      typedef decltype(fpmin_wrong(d, d)) fpmin_wrong_return_type1; // double&
      typedef decltype(fpmin_wrong(i, d)) fpmin_wrong_return_type2; // double
      typedef decltype(fpmin_wrong(j, d)) fpmin_wrong_return_type3; // double
    
      typedef decltype(fpmin(d, d)) fpmin_return_type1; // double
      typedef decltype(fpmin(i, d)) fpmin_return_type2; // double
      typedef decltype(fpmin(j, d)) fpmin_return_type3; // double
    }
  • decltype은 실제로 실행시키지 않고 추론만 하므로, 유효하지 않음에도 사용할 수 있는 경우가 있다.

    #include <vector>
    
    int            main()
    {
      std::vector<int> vec;
      typedef decltype(vec[0]) integer; // int&
    }
  • decltypenested type에 접근하기에도 유용하다.

    template <typename R>
    class SomeFunctor
    {
    public:
      typedef R result_type;
    };
    
    int            main()
    {
      SomeFunctor<int> func;
      typedef decltype(func)::result_type integer; // int
    }

lambda 함수에도 적용이 가능하다

  • 람다 함수 역시 decltype에서 소괄호로 한번 더 감싸주면 L-value 레퍼런스로 추론된다.

    #include <iostream>
    
    int            main()
    {
      using namespace std;
    
      auto lambda = []() { return 42; };
      decltype(lambda) lambda2(lambda);
      decltype((lambda)) lambda3(lambda);
    
      cout << &lambda << '\n' << &lambda2 << '\n' << &lambda3 << endl;
    }
    
    /* stdout
    0053F717
    0053F70B
    0053F717
    */
  • 람다 함수의 파라미터에는 auto 키워드가 올 수 있다.

    #include <iostream>
    
    int            main()
    {
      using namespace std;
    
      auto lambda = [](auto x, auto y)
      {
        return x + y;
      };
    
      cout << lambda(1.1, 2) << endl;
      cout << lambda(3, 4) << endl;
      cout << lambda(4.5, 2.2) << endl;
    }
    
    /* stdout
    3.1
    7
    6.7
    */

'C++ > Syntax' 카테고리의 다른 글

C++ 열거형 (Enumerate Type)  (0) 2021.03.10
C++ 형변환 (Type Conversion)  (0) 2021.03.10
C++ using  (0) 2021.03.10
C++ extern  (0) 2021.03.10
C++ 범위 지정 연산자 (Scope Resolution Operator)  (0) 2021.03.10
C++/Syntax 2021. 3. 10. 21:09

using

  • using std::cout; 이라고 선언해두면 cout만 써도 std::cout을 사용할 수 있다.

    • 편하지만 남발하면 모호성이 생겨서 컴파일이 안될 수 있다.

      • 영역을 최소한으로 잡아서 사용할 것
  • 상속의 접근 권한을 변경할 때도 사용할 수 있다.

'C++ > Syntax' 카테고리의 다른 글

C++ 형변환 (Type Conversion)  (0) 2021.03.10
C++ 자료형 추론 (auto, decltype)  (0) 2021.03.10
C++ extern  (0) 2021.03.10
C++ 범위 지정 연산자 (Scope Resolution Operator)  (0) 2021.03.10
C++ 비트 연산자 (Bitwise Operator)  (0) 2021.03.10
C++/Syntax 2021. 3. 10. 21:07

extern

  • 함수의 경우 기본적으로 extern이 생략되어 있는 것이다.

  • 변수의 경우 가져다 쓰는 파일에서 forward declaration 할 때 앞에 extern을 붙여주어야 한다.

    • 이를 통해 External Linkage 속성을 가진다.

    main.cpp

    #include <iostream>
    
    extern int a;  // OK
    int a;         // Linking Error
    
    int main()
    {
      using namespace std;
    
      cout << a << endl;
    }

    variable.cpp

    extern int a = 100;  // OK
    int a = 100;         // OK

메모리 절약 팁 (extern)

  • 헤더 파일의 namespace에서 const 키워드로 상수를 만들면 파일마다 메모리를 따로 사용하여 낭비하는 꼴이 된다.(아래 예제 참고)

  • 원인

    • #include 는 파일의 내용을 그대로 갖다붙인다.

    • namespace 안에 존재하기는 하지만 각각의 파일에서 전역 변수로 작동한다.

    • const 키워드는 기본적으로 Internal Linkage 속성이다.

    • 따라서 같은 전역변수 이름을 쓰더라도 const 덕분에 충돌이 일어나지 않는다.

    • 결국 다른 주소 공간에 있는 데이터를 불러오는 것과 같다.

    main.cpp

    #include <iostream>
    #include "MyConstants.h"
    
    void test();
    
    int main()
    {
      using namespace std;
    
      cout << Constants::pi << ' ' << &Constants::pi << endl;
      test();
    }

    MyConstants.h

    #pragma once
    
    namespace Constants
    {
      const double pi(3.141592);
      const double gravity(9.8);
    }

    test.cpp

    #include <iostream>
    #include "MyConstants.h"
    
    void test()
    {
      using namespace std;
    
      cout << Constants::pi << ' ' << &Constants::pi << endl;
    }
  • 이를 방지하기 위해서는 별도의 MyConstants.cpp 파일을 생성한 후, extern 키워드로 내보내야 한다.

    • 위의 코드를 아래와 같이 변경하면 같은 메모리 공간에 접근하는 것을 알 수 있다.

    main.cpp

    #include <iostream>
    #include "MyConstants.h"
    
    void test();
    
    int main()
    {
      using namespace std;
    
      cout << Constants::pi << ' ' << &Constants::pi << endl;
      test();
    }

    MyConstants.h

    #pragma once
    
    namespace Constants
    {
      extern const double pi;
      extern const double gravity;
    }

    MyConstants.cpp

    namespace Constants
    {
      extern const double pi(3.141592);
      extern const double gravity(9.8);
    }

    test.cpp

    #include <iostream>
    #include "MyConstants.h"
    
    void test()
    {
      using namespace std;
    
      cout << Constants::pi << ' ' << &Constants::pi << endl;
    }

'C++ > Syntax' 카테고리의 다른 글

C++ 자료형 추론 (auto, decltype)  (0) 2021.03.10
C++ using  (0) 2021.03.10
C++ 범위 지정 연산자 (Scope Resolution Operator)  (0) 2021.03.10
C++ 비트 연산자 (Bitwise Operator)  (0) 2021.03.10
C++ 쉼표 연산자 (Comma Operator)  (0) 2021.03.10
C++/Syntax 2021. 3. 10. 21:05

범위 지정 연산자 (Scope Resolution Operator)

  • ::

  • 클래스, 함수 이름 충돌을 해결할 수 있다.

  • 앞에 아무것도 안쓰면 전역 변수를 불러올 수 있다.

    #include <iostream>
    
    int value = 123;
    
    int main()
    {
      std::cout << value << '\n';  // 123
      int value = 1;
      std::cout << value << '\n';  // 1
      std::cout << ::value << '\n';  // 123
    }

  • C++17

    • nested namespace 대신 outer::inner::more::...의 형식으로 확장하여 바로 정의할 수도 있다.

    • nested namespace는 헷갈리므로 반드시 필요한 경우가 아니면 지양하는게 좋다고 한다.

'C++ > Syntax' 카테고리의 다른 글

C++ using  (0) 2021.03.10
C++ extern  (0) 2021.03.10
C++ 비트 연산자 (Bitwise Operator)  (0) 2021.03.10
C++ 쉼표 연산자 (Comma Operator)  (0) 2021.03.10
C++ 산술 연산자 (Arithmetic Operator)  (0) 2021.03.10
C++/Syntax 2021. 3. 10. 21:04

비트 연산자 (Bitwise Operator)

  • 빠르다.

  • <bitset> 라이브러리를 통해 비트를 그대로 출력 가능하다.

    unsigned int a = 3;
    std::cout << std::bitset<4>(a) << '\n';       // 0011
    std::cout << std::bitset<4>(a << 1) << '\n';  // 0110

'C++ > Syntax' 카테고리의 다른 글

C++ extern  (0) 2021.03.10
C++ 범위 지정 연산자 (Scope Resolution Operator)  (0) 2021.03.10
C++ 쉼표 연산자 (Comma Operator)  (0) 2021.03.10
C++ 산술 연산자 (Arithmetic Operator)  (0) 2021.03.10
C++ 상수 (Constant)  (0) 2021.03.10
C++/Syntax 2021. 3. 10. 21:04

쉼표 연산자 (Comma Operator)

  • 순차적으로 계산한 뒤 마지막 계산 결과를 반환한다.

  • 일반적으로 잘 쓰이지 않고, for문에서나 가끔 쓰이는 정도라고 한다.

    int x = 3;
    int y = 10;
    int z = (++x, ++y);
    int i, j;
    for (i = 1, j = 1; i < 5; ++i, j += 2)
        myFunc((j--, i + 2), z);
C++/Syntax 2021. 3. 10. 21:04

산술 연산자 (Arithmetic Operator)

음수 나누기

  std::cout << -5 / 2 << " " << -5 % 2 << '\n';  // 출력결과 : -2 -1

아래와 같이 작성 금지(애매함)

  int x = 1;
  x = x++;
  addTwoNum(x, ++x);

'C++ > Syntax' 카테고리의 다른 글

C++ 비트 연산자 (Bitwise Operator)  (0) 2021.03.10
C++ 쉼표 연산자 (Comma Operator)  (0) 2021.03.10
C++ 상수 (Constant)  (0) 2021.03.10
C++ 형 변환 (Type Casting)  (0) 2021.03.09
C++ Boolean  (0) 2021.03.09
C++/Syntax 2021. 3. 10. 21:01

상수 (Constant)

Literal constant

  • 숫자, 문자 등 메모리에 존재하지 않아도 사용 가능한 값

  • Binary Literal

    • C++14

      0b1011'1111'1010

Symbolic constant

  • Compile Time constant

    • 컴파일할 때 값이 결정되는 상수

    • constexpr

      • C++11

      • 컴파일 타임에 값이 결정되는 상수에만 사용 가능한 키워드이다.

  • Runtime constant

    • 런타임에 값이 결정되는 상수

매크로

  • C 스타일에서는 상수를 매크로로 정의했지만, C++에서는 그렇게 사용하지 않는다고 한다.

    • 이유

      • 디버깅이 귀찮아진다.

      • 적용 범위가 너무 넓다.

    • 대신에 함수 내에서 const 형식으로 작성하는 것이 일반적이라고 한다.


const_cast

  • const로 지정한 상수의 값을 변경할 때 사용한다.

    #include <iostream>
    
    int     main()
    {
      using namespace std;
    
      const double gravity{ 9.8 };
    
      double* temp = const_cast<double*>(&gravity);
      cout << &gravity << ' ' << temp << endl;
    
      *temp = 123.0;
    
      cout << &gravity << ' ' << temp << endl;
      cout << gravity << ' ' << *temp << endl;
    }
    
    /* stdout stderr
    000DDFC84 00DDFC84
    00DDFC84 00DDFC84
    9.8 123
    */
    • 같은 주소를 사용하는데 다른 값이 나온다...

    • memcpy 함수를 사용해도 변하지 않는다.

      #include <iostream>
      
      int     main()
      {
        using namespace std;
      
        const double gravity{ 9.8 };
      
        cout << gravity << endl;  // 9.8
      
        const double temp = 1024.0;
      
        memcpy(const_cast<double*>(&gravity), &temp, sizeof(double));
      
        cout << gravity << endl;  // 9.8
      }
      
      /* stdout stderr
      9.8
      9.8
      */
    • 이렇게 사용하지 않는게 좋을 것 같다.


클래스에서의 const

클래스를 const로 인스턴스화 (instanciation)

  • 멤버 함수를 정의할 때, 값을 변경하지 않는 함수면 컴파일러가 알 수 있도록 함수 정의 시 const 키워드를 붙이는게 좋다.

  • 멤버 변수들을 수정할 수 없다. 모두 const 취급하는 것이다.

    int    getValue() const
    {
      return value_;
    }

함수 파라미터에 const와 레퍼런스를 같이 붙이는 이유

  • 다음 예제에서 print 함수의 파라미터는 복사 생성자를 호출한다.

    #include <iostream>
    
    class Something
    {
      int value_ = 0;
    
    public:
      Something()
      {
        std::cout << "Constructor\n";
      }
    
      int    getValue() const
      { 
        return value_;
      }
    };
    
    void    print(Something s)
    {
      std::cout << &s << std::endl;
      std::cout << s.getValue() << std::endl;
    }
    
    int        main()
    {
      const Something s;
    
      std::cout << &s << std::endl;
      print(s);
    }
    
    /* stdout
    Constructor
    012FFB9C
    012FFAC8
    0
    */
    • "Constructor" 메시지가 한 번만 출력된 것을 볼 수 있다.

      • 위에 정의된 생성자가 아닌, 기본 복사 생성자를 호출했기 때문이다.
  • 이렇게 복사하는건 오버헤드가 크기 때문에 다음과 같이 원본으로 가져오는게 일반적이다.

    #include <iostream>
    
    class Something
    {
      int value_ = 0;
    
    public:
      Something()
      {
        std::cout << "Constructor\n";
      }
    
      int    getValue() const
      { 
        return value_;
      }
    };
    
    void    print(const Something& s)
    {
      std::cout << &s << std::endl;
      std::cout << s.getValue() << std::endl;
    }
    
    int        main()
    {
      const Something s;
    
      std::cout << &s << std::endl;
      print(s);
    }
    
    /* stdout
    Constructor
    00AFFB78
    00AFFB78
    0
    */
  • const 키워드 유무에 따라 오버로딩이 다르게 적용된다.

    • 원래 같은 파라미터를 갖는 같은 이름의 함수는 두 개 이상 정의할 수 없으나, const가 붙는 함수는 다른 함수 취급한다.

      #include <iostream>
      
      class Something
      {
          int value_ = 0;
      
      public:
          const int& getValue() const
          {
              std::cout << "const version : " << value_ << '\n';
              return value_;
          }
      
          int& getValue()
          {
              std::cout << "non-const version : " << value_ << '\n';
              return value_;
          }
      };
      
      int        main()
      {
          Something       s1;
          const Something s2;
      
          s2.getValue();
          s1.getValue() = 100;
          s1.getValue();
      }
      
      /* stdout
      const version : 0
      non-const version : 0
      non-const version : 100
      */
    • 다음과 같이 const를 빼고 getValue 함수를 작성하면 오버로딩 오류가 발생한다.

      public:
      const int&    getValue()  // 여기 const가 없으면 오류
      { 
        std::cout << "const version : " << value_ << '\n';
        return value_;
      }
      
      int&        getValue()
      {
        std::cout << "non-const version : " << value_ << '\n';
        return value_;
      }
      error C2556: 'int &Something::getValue(void)': overloaded function differs only by return type from 'const int &Something::getValue(void)'
    • 다음과 같이 반환 값에 const가 없어도 오류가 발생한다.

      int&    getValue() const
      { 
        std::cout << "const version : " << value_ << '\n';
        return value_;
      }
      error C2440: 'return': cannot convert from 'const int' to 'int &'
      • int& getValue() const에서 오른쪽 const에 의해 내부 변수 값들이 const 취급된다. (여기서는 const int)

      • 그런데 반환 값은 int& 형태의 레퍼런스이므로 자료형이 맞지 않아서 발생하는 오류이다.

      • 반환 자료형을 레퍼런스 없이 int로만 쓰면 오류는 없지만, 앞에도 const를 붙여주는게 맞는 것 같다.

'C++ > Syntax' 카테고리의 다른 글

C++ 쉼표 연산자 (Comma Operator)  (0) 2021.03.10
C++ 산술 연산자 (Arithmetic Operator)  (0) 2021.03.10
C++ 형 변환 (Type Casting)  (0) 2021.03.09
C++ Boolean  (0) 2021.03.09
C++ 소수점 (Decimal Point)  (0) 2021.03.09
C++/Syntax 2021. 3. 9. 19:37

형변환 (Type Casting)

변환 방식

  • C Style
    (char)65
  • C++ Style
    char(65)
    static_cast<char>(65)

numeric promotion

  • 크기가 작은 자료형에서 큰 자료형으로 형 변환하는 것

    ex) float -> double


절삭(truncation)

  int    i = 30000;  // 0b111010100110000
  short s = i;    // 1바이트만 저장되어 0b00110000, 즉 48

  double d = 0.123456789;
  float f = d;  // 정밀도가 부족해서 다른 값이 저장됨

묵시적 형변환(implicit type conversion)에서의 자료형 별 우선 순위

  long double (highest)
  double
  float
  unsigned long long
  long long
  unsigned long
  long
  unsigned int
  int (lowest)
  char
  bool

'C++ > Syntax' 카테고리의 다른 글

C++ 산술 연산자 (Arithmetic Operator)  (0) 2021.03.10
C++ 상수 (Constant)  (0) 2021.03.10
C++ Boolean  (0) 2021.03.09
C++ 소수점 (Decimal Point)  (0) 2021.03.09
C++ 자료형 (Data Type)  (0) 2021.03.09
C++/Syntax 2021. 3. 9. 19:36

Boolean

true, false 출력

  bool b{true};
  std::cout << std::boolalpha;
  std::cout << b << '\n';
  std::cout << std::noboolalpha;
  std::cout << b << '\n';

'C++ > Syntax' 카테고리의 다른 글

C++ 상수 (Constant)  (0) 2021.03.10
C++ 형 변환 (Type Casting)  (0) 2021.03.09
C++ 소수점 (Decimal Point)  (0) 2021.03.09
C++ 자료형 (Data Type)  (0) 2021.03.09
C++ 전처리기 (Preprocessor)  (0) 2021.03.09
C++/Syntax 2021. 3. 9. 19:36

소수점 (Decimal Point)

고정 소수점 (정수)

  • 부호화 절대치

    • 맨 왼쪽 비트가 부호를 결정하고, 나머지 비트는 일반 양수를 다루듯 계산한다.
    000 -> +0
    001 -> +1
    010 -> +2
    011 -> +3
    100 -> -0
    101 -> -1
    110 -> -2
    111 -> -3
    • 안쓰는 이유

      • 순환성이 떨어진다.(+3 다음이 -0)

      • 보수를 사용하면 감산기 없이 가산기만으로 뺄셈이 가능하다.

  • 1의 보수

    • 비트를 반전시켜 순환성을 가지도록 했다.(+3 다음이 -3)

    • 덧셈기로 뺄셈이 가능하다.

    000 -> +0
    001 -> +1
    010 -> +2
    011 -> +3
    100 -> -3
    101 -> -2
    110 -> -1
    111 -> -0
  • 2의 보수

    • 1의 보수를 취하고 1을 더하는 형태이다.

    • -0을 없애고 숫자를 하나 더 챙겼다.

    • 빨리 만드는 방법

      • 오른쪽부터 왼쪽 방향으로 1을 만날 때까지 그대로, 그 이후는 반전시키면 된다.
    000 -> +0
    001 -> +1
    010 -> +2
    011 -> +3
    100 -> -4
    101 -> -3
    110 -> -2
    111 -> -1

부동 소수점(실수)

  • 부호 비트, 지수부, 가수부로 구성된다.

    • single-precision(float) : 지수부(8비트), 가수부(23비트)

    • double-precision(double) : 지수부(11비트), 가수부(52비트)

  • (-1)^(부호비트) * (1.(가수부)) * 2^(지수부 - 127)의 값을 가진다.

    • double-precision의 경우 127(0b01111111)이 아닌 1023(0b01111111111)

    • 참고

  • 출력 시 precision 조절하기

    double d(0.1);
    
    std::cout << std::setprecision(16);
    std::cout << d << std::endl;  // 0.10000000000000001
  • 오차는 부동 소수점의 한계이다.

    double d(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1);
    
    std::cout << std::setprecision(16);
    std::cout << d << std::endl;  // 0.99999999999999989
  • 0으로 나누는 등 수학적인 오류가 발생할 가능성이 있는 경우

    • 실수를 0으로 나누면 inf이다.

    • 0을 0으로 나누면 nan이다.

    • <cmath> 라이브러리의 std::isinf, std::isnan으로 체크해야 한다.


float vs double

  • 의외로 연산 속도는 별 차이가 없다.

  • 메모리가 중요하다면 float, 정밀도가 중요하다면 double을 사용하는게 좋을 듯 하다.

  • 오차 예제

    #include <iostream>
    #include <iomanip>
    
    int     main()
    {
      using namespace std;
    
      const float f_begin = 1e9f;
      float f = f_begin;
      cout << setprecision(10000);
      cout << f << endl;    // 1000000000
      for (long long i = 0; i < 1e6; ++i)
        f += +1e0f;
      cout << f << "\n\n";  // 1000000000
    
      const double d_begin = 1e9f;
      double d = d_begin;
      cout << d << endl;    // 1000000000
      for (long long i = 0; i < 1e6; ++i)
        d += +1e0;
      cout << d << endl;    // 1001000000
    }
    • 근본적인 해결은 아니지만 임시로 해결하는 방법

      • 작은 연산은 따로 수행한 뒤 한번에 합치는 식으로 하면 된다.

        #include <iostream>
        #include <iomanip>
        
        int     main()
        {
            using namespace std;
        
            const float f_begin = 1e9f;
            float f = f_begin;
            cout << setprecision(10000);
            cout << f << endl;    // 1000000000
            float f2 = 0.0f;
            for (long long i = 0; i < 1e6; ++i)
                f2 += +1e0f;
            f += f2;
            cout << f << "\n\n";  // 1001000000
        }

'C++ > Syntax' 카테고리의 다른 글

C++ 형 변환 (Type Casting)  (0) 2021.03.09
C++ Boolean  (0) 2021.03.09
C++ 자료형 (Data Type)  (0) 2021.03.09
C++ 전처리기 (Preprocessor)  (0) 2021.03.09
C++ Namespace  (0) 2021.03.09
C++/Syntax 2021. 3. 9. 19:35

자료형 (Data Type)

<limits> 라이브러리

  • 자료형의 최대, 최소 확인

    std::cout << std::numeric_limits<short>::max() << '\n';
    std::cout << std::numeric_limits<short>::min() << '\n';
    std::cout << std::numeric_limits<short>::lowest() << '\n';
  • min, lowest 차이

    • min : the smallest finite number that is > 0 representable in the type
      • 즉 표현할 수 있는 정밀도의 단위이다.
    • lowest : the smallest finite number that is representable
      • 흔히 말하는 최솟값이다.

고정 너비 정수(Fixed-width Integer)

  • C++11

  • <cstdint> 라이브러리(<iostream>에 포함)

  • fast type(std::int_fast#_t)

    • 최소 #비트의 너비로 가장 빠른 타입을 제공한다.
  • least type(std::int_least#_t)

    • 최소 #비트의 너비로 가장 작은 타입을 제공한다.
  • 지양할 것

    • std::int8_t

      • char로 처리되기 때문에 출력 시 ASCII로 변환되어 나온다.

'C++ > Syntax' 카테고리의 다른 글

C++ Boolean  (0) 2021.03.09
C++ 소수점 (Decimal Point)  (0) 2021.03.09
C++ 전처리기 (Preprocessor)  (0) 2021.03.09
C++ Namespace  (0) 2021.03.09
C++ 헤더가드 (Header Guard)  (0) 2021.03.09
C++/Syntax 2021. 3. 9. 19:20

전처리기 (Preprocessor)

기능

  • 파일 포함시키기

    • #include

    • 특정 파일을 현재 위치에 첨부하여 하나의 파일처럼 컴파일한다.

  • 조건부 컴파일

    • #if, #elif, #else, #ifdef, #ifndef, #endif

    • 조건에 해당되는 코드만 실행한다.

    • defined 키워드와 같이 사용하여 복합적으로 정의할 수 있다.

      • OS에 따라 다른 파일을 포함시키는 예제

        #ifdef __unix__ /* __unix__ is usually defined by compilers targeting Unix systems */
        # include <unistd.h>
        #elif defined _WIN32 /* _Win32 is usually defined by compilers targeting 32 or 64 bit Windows systems */
        # include <windows.h>
        #endif
      • 32, 64비트에 따라 다른 동작을 하는 예제

        #if !(defined __LP64__ || defined __LLP64__) || defined _WIN32 && !defined _WIN64
          // we are compiling for a 32-bit system
        #else
          // we are compiling for a 64-bit system
        #endif
  • 매크로

    • #define, #undef

    • 컴파일 중 토큰이 나오면 바로 해당 값으로 바꾸어 컴파일한다. (단순한 문자 교환)

    • 정의한 파일 내부에서만 적용된다.

    • 대상변환형(object-like macros) : 인수 X

    • 유사함수변환형(function-like macros) : 인수 O

      • 매크로 정의

        #define <identifier> <replacement token list>                    // object-like macro
        #define <identifier>(<parameter list>) <replacement token list>  // function-like macro, note parameters
      • 매크로 삭제

        #undef <identifier>  // delete the macro
    • ## 토큰 연결 연산자

      • 두 개의 토큰을 하나로 연결

        #define DECLARE_STRUCT_TYPE(name) typedef struct name##_s name##_t
        
        DECLARE_STRUCT_TYPE(g_object); // 변환 결과는 typedef struct g_object_s g_object_t;
  • #pragma

    • 컴파일러에게 특정 옵션이나 라이브러리 형태 등을 지정할 수 있다.

참고

'C++ > Syntax' 카테고리의 다른 글

C++ 소수점 (Decimal Point)  (0) 2021.03.09
C++ 자료형 (Data Type)  (0) 2021.03.09
C++ Namespace  (0) 2021.03.09
C++ 헤더가드 (Header Guard)  (0) 2021.03.09
C++ 초기화 (Initialization)  (0) 2021.03.09