C++/Class 2021. 3. 16. 00:09

friend

  • 클래스 내부에다가 friend 키워드 다음에 다른 클래스나 함수를 선언하면, 해당 클래스나 함수에서 private 멤버에 접근할 수 있다.

예제

  • 간단한 예제

    #include <iostream>
    
    class A
    {
      int value_ = 1;
    
      friend void doSomething(A& a);
    };
    
    void doSomething(A& a)
    {
      std::cout << a.value_ << std::endl;
    }
    
    int        main()
    {
      A    a;
    
      doSomething(a);
    }
    
    /* stdout
    1
    */
  • 조금 귀찮은 예제

    #include <iostream>
    
    class A;
    
    class B
    {
      int    value_ = 2;
    
    public:
      void doSomething(A& a)
      {
        std::cout << a.value_ << std::endl;  // 에러
      }
    };
    
    class A
    {
      int value_ = 1;
    
      friend class B;
    };
    
    int        main()
    {
      using namespace std;
    
      A    a;
      B    b;
    
      b.doSomething(a);
    }
    error C2027: use of undefined type 'A'
    • class A라고 전방 선언을 해도, class A 내부의 멤버 변수들은 알 수 없으므로 에러가 발생한다.

    • 해결법 : 함수의 정의와 선언을 분리하여 class A의 아래 쪽에 정의한다.

    #include <iostream>
    
    class A;
    
    class B
    {
      int    value_ = 2;
    
    public:
      void doSomething(A& a);
    };
    
    class A
    {
      int value_ = 1;
    
      friend class B;
    };
    
    void B::doSomething(A& a)
    {
      std::cout << a.value_ << std::endl;
    }
    
    int        main()
    {
      using namespace std;
    
      A    a;
      B    b;
    
      b.doSomething(a);
    }

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

C++ Nested Types  (0) 2021.03.16
C++ 익명 객체 (Anonymous Class)  (0) 2021.03.16
C++ 정적 멤버 함수 (Static Member Function)  (0) 2021.03.16
C++ 정적 멤버 변수 (Static Member Variable)  (0) 2021.03.15
C++ static (Class)  (0) 2021.03.15
C++/Class 2021. 3. 16. 00:05

정적 멤버 함수 (Static Member Function)

  • 클래스의 멤버 함수들은 한 번만 정의하고, this를 통해 인스턴스의 주소를 포함해서 전달한다고 했다.

    • 인스턴스 없이 범위 지정 연산자로 바로 멤버 함수를 사용하고자 할 때, 굳이 static을 붙여야 동작할 수 있는건가 싶어서 간단한 예제를 만들어 보았다.

      #include <iostream>
      
      class Something
      {
        static int    s_value_;
      
      public:
        int    getValue()
        {
          return s_value_;
        }
      };
      
      int Something::s_value_ = 1;
      
      int        main()
      {
        using namespace std;
      
        cout << Something::getValue();  // 에러
      }
      error C2352: 'Something::getValue': illegal call of non-static member function
      • 내부적으로 인스턴스의 주소를 인자로 전달하는 원리인데, 범위 지정 연산자로 함수를 사용할 경우 인스턴스의 주소가 없으므로 에러가 발생하는 것이다.

예제

  • static 멤버 함수에는 this 포인터가 인자로 들어가지 않는다.

    • 즉, this 포인터로 접근해야하는 모든게 안된다.

    • 정적으로 메모리에 존재하는 변수에는 접근할 수 있지만, static 변수가 아닌 일반 변수에는 접근할 수 없다.

    #include <iostream>
    
    class Something
    {
      static int    s_value_;
    
    public:
      static int    getValue()
      {
        return s_value_;
      }
    };
    
    int Something::s_value_ = 1;
    
    int        main()
    {
      using namespace std;
    
      cout << Something::getValue() << endl;
    
      Something    s1;
    
      cout << s1.getValue() << endl;
    }
    
    /* stdout
    1
    1
    */
  • 함수 포인터를 통한 이해

    • 일반 멤버 함수를 저장하는 예제

      #include <iostream>
      
      class Something
      {
        static int    s_value_;
      
      public:
        static int    getValue()
        {
          return s_value_;
        }
      
        int            temp()
        {
          return s_value_;
        }
      };
      
      int Something::s_value_ = 1;
      
      int        main()
      {
        using namespace std;
      
        Something    s1, s2;
      
        cout << s1.getValue() << endl;
      
        int    (Something:: * fptr1)() = &Something::temp;
      
        cout << (s2.*fptr1)() << endl;
      }
      
      /* stdout
      1
      1
      */
    • 정적 멤버 함수를 저장하는 예제

      #include <iostream>
      
      class Something
      {
        static int    s_value_;
      
      public:
        static int    getValue()
        {
          return s_value_;
        }
      
        int            temp()
        {
          return s_value_;
        }
      };
      
      int Something::s_value_ = 1;
      
      int        main()
      {
        using namespace std;
      
        Something    s1, s2;
      
        cout << s1.getValue() << endl;
      
        int    (Something:: * fptr2)() = &Something::getValue;  // 에러
      
        cout << (s2.*fptr2)() << endl;
      }
      error C2440: 'initializing': cannot convert from 'int (__cdecl *)(void)' to 'int (__thiscall Something::* )(void)'
      • 즉, 여기서의 Something::getValue 함수는 int (*)(void) 형태의 함수인 것을 알 수 있다.

      • fptr2의 형태를 바꾸면 작동된다.

      #include <iostream>
      
      class Something
      {
        static int    s_value_;
      
      public:
        static int    getValue()
        {
          return s_value_;
        }
      
        int            temp()
        {
          return s_value_;
        }
      };
      
      int Something::s_value_ = 1;
      
      int        main()
      {
        using namespace std;
      
        Something    s1, s2;
      
        cout << s1.getValue() << endl;
      
        int    (*fptr2)() = &Something::getValue;
      
        cout << fptr2() << endl;
      }
      /* stdout
      1
      1
      */

요약

  • static 멤버 함수는 this 포인터 인자가 들어가지 않는다.

    • 인스턴스를 특정할 수 없으므로 static 멤버 변수만 호출 가능하다.

    • 범위 지정 연산자로 정적 멤버 함수를 사용할 수 있다.

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

C++ 익명 객체 (Anonymous Class)  (0) 2021.03.16
C++ friend  (0) 2021.03.16
C++ 정적 멤버 변수 (Static Member Variable)  (0) 2021.03.15
C++ static (Class)  (0) 2021.03.15
C++ 체이닝(Chaining)  (0) 2021.03.15
C++/Class 2021. 3. 15. 23:59

정적 멤버 변수 (Static Member Variable)

  • 클래스 내부에 static 변수를 정의할 때는 직접 초기화할 수 없다.

    • 중복 선언 문제인 것 같다.

    • 생성자에서도 초기화할 수 없다.

      • Inner Class를 사용해서 우회하여 초기화할 수는 있다.

예제

  • static이 아닌 기본 예제

    #include <iostream>
    
    class Something
    {
    public:
      int    value_ = 1;
    };
    
    int        main()
    {
      using namespace std;
    
      Something st1;
      Something st2;
    
      st1.value_ = 2;
    
      cout << &st1.value_ << ' ' << st1.value_ << endl;
      cout << &st2.value_ << ' ' << st2.value_ << endl;
    }
    
    /* stdout
    00CFFB4C 2
    00CFFB40 1
    */
  • static 예제

    #include <iostream>
    
    class Something
    {
    public:
      static int    s_value_;
    };
    
    int Something::s_value_ = 1;  // define in .cpp file(not in header file)
    
    int        main()
    {
      using namespace std;
    
      cout << &Something::s_value_ << ' ' << Something::s_value_ << endl;
    
      Something st1;
      Something st2;
    
      st1.s_value_ = 2;
    
      cout << &st1.s_value_ << ' ' << st1.s_value_ << endl;
      cout << &st2.s_value_ << ' ' << st2.s_value_ << endl;
    
      Something::s_value_ = 1024;
    
      cout << &Something::s_value_ << ' ' << Something::s_value_ << endl;
    }
    
    /* stdout
    0032C008 1
    0032C008 2
    0032C008 2
    0032C008 1024
    */
    • 클래스 외부에서 정의해야 한다는 점이 좀 헷갈린다.

Inner Class

  • Inner Class를 사용한 static 변수의 클래스 내부 초기화

    #include <iostream>
    
    class Something
    {
    public:
      class _init
      {
      public:
        _init()
        {
          s_value_ = 9876;
        }
      };
    
    private:
      static int        s_value_;
      static _init      s_initializer;
    
    public:
      static int    getValue()
      {
        return s_value_;
      }
    };
    
    int                    Something::s_value_;
    Something::_init    Something::s_initializer;
    
    int        main()
    {
      using namespace std;
    
      Something    s1;
    
      cout << s1.getValue() << endl;
    }
    
    /* stdout
    9876
    */

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

C++ friend  (0) 2021.03.16
C++ 정적 멤버 함수 (Static Member Function)  (0) 2021.03.16
C++ static (Class)  (0) 2021.03.15
C++ 체이닝(Chaining)  (0) 2021.03.15
C++ this  (0) 2021.03.15
C++/Class 2021. 3. 15. 23:58

static

정적 멤버 변수 (Static Member Variable)

정적 멤버 함수 (Static Member Function)

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

C++ 정적 멤버 함수 (Static Member Function)  (0) 2021.03.16
C++ 정적 멤버 변수 (Static Member Variable)  (0) 2021.03.15
C++ 체이닝(Chaining)  (0) 2021.03.15
C++ this  (0) 2021.03.15
C++ 소멸자 (Destructor)  (0) 2021.03.15
C++/Class 2021. 3. 15. 23:39

체이닝(Chaining)

  • 함수의 반환 값을 해당 인스턴스의 레퍼런스로 전달하여, 연속해서 함수를 사용할 수 있도록 하는 기법이다.

  • 아래와 같이 함수를 하나씩 작성하면 귀찮다.

    #include <iostream>
    
    class Calc
    {
      int    value_;
    
    public:
      Calc(const int &value_in)
        : value_{value_in}
      {}
    
      void add(int value) { value_ += value; }
      void sub(int value) { value_ -= value; }
      void mul(int value) { value_ *= value; }
      void print()
      {
        std::cout << value_ << '\n';
      }
    };
    
    int        main()
    {
      Calc cal{ 10 };
    
      cal.add(10);
      cal.sub(1);
      cal.mul(2);
      cal.print();
    }
  • 체이닝을 적용하면 편하다.

    #include <iostream>
    
    class Calc
    {
      int    value_;
    
    public:
      Calc(const int &value_in)
        : value_{value_in}
      {}
    
      Calc& add(int value) { value_ += value; return *this; }
      Calc& sub(int value) { value_ -= value; return *this; }
      Calc& mul(int value) { value_ *= value; return *this; }
      void print()
      {
        std::cout << value_ << '\n';
      }
    };
    
    int        main()
    {
      Calc cal{ 10 };
    
      cal.add(10).sub(1).mul(2).print();
    }

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

C++ 정적 멤버 변수 (Static Member Variable)  (0) 2021.03.15
C++ static (Class)  (0) 2021.03.15
C++ this  (0) 2021.03.15
C++ 소멸자 (Destructor)  (0) 2021.03.15
C++ 변환 생성자(Converting Constructor)  (0) 2021.03.15
C++/Class 2021. 3. 15. 23:38

this

  • 인스턴스 객체의 주소를 가리킨다.

예제

  #include <iostream>

  class Simple
  {
    int    id_;

  public:
    Simple(const int& id_in)
    {
      setID(id_in);

      std::cout << this << std::endl;
    }

    void    setID(int id) { id_ = id; }
    int    getID() { return id_; }
  };

  int        main()
  {
    Simple s1{ 1 }, s2{ 2 };

    s1.setID(2);
    s2.setID(4);
    std::cout << &s1 << '\n' << &s2 << std::endl;
  }

  /* stdout
  00EFFD14
  00EFFD08
  00EFFD14
  00EFFD08
  */
  • 인스턴스를 생성할 때 멤버 함수들을 따로따로 만드는게 아니라, 한번 정의한 것을 계속 사용하는게 효율적이다.
  • 이를 위해서는 인스턴스의 주소를 멤버 함수가 사용될 때 전달해야 한다.
  • 위 예제의 main 함수에서 s1.setID(2) 명령줄은 다음과 같이 작동하는 것이다.
    • 첫 인자로 인스턴스의 주소를 넣는 방식이다.
  • Simple::setID(&s1, 2);
  • -> : 포인터의 member selection operator
  • 체이닝(Chaining)

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

C++ static (Class)  (0) 2021.03.15
C++ 체이닝(Chaining)  (0) 2021.03.15
C++ 소멸자 (Destructor)  (0) 2021.03.15
C++ 변환 생성자(Converting Constructor)  (0) 2021.03.15
C++ 복사 생성자(Copy Constructor)  (0) 2021.03.12
C++/Class 2021. 3. 15. 23:34

소멸자 (Destructor)

  • 인스턴스가 메모리에서 해제될 때 내부에서 호출되는 함수

    • 메모리 누수를 방지할 때 필수적이다.
    #include <iostream>
    
    class Simple
    {
      int        num_;
    
    public:
      Simple(const int& num_in)
        : num_{num_in}
      {
        std::cout << "Constructor " << num_ << '\n';
      }
    
      ~Simple()
      {
        std::cout << "Destructor " << num_ << '\n';
      }
    };
    
    int        main()
    {
      {
        Simple s0{ 0 };
        Simple s1{ 1 };
      }
      std::cout << std::endl;
      {
        Simple *s0 = new Simple{ 0 };
        Simple s1{ 1 };
    
        delete s0;
      }
    }
    
    /* stdout
    Constructor 0
    Constructor 1
    Destructor 1
    Destructor 0
    
    Constructor 0
    Constructor 1
    Destructor 0
    Destructor 1
    */

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

C++ 체이닝(Chaining)  (0) 2021.03.15
C++ this  (0) 2021.03.15
C++ 변환 생성자(Converting Constructor)  (0) 2021.03.15
C++ 복사 생성자(Copy Constructor)  (0) 2021.03.12
C++ 위임 생성자 (Delegating Constructor)  (0) 2021.03.12
C++/Class 2021. 3. 15. 23:32

변환 생성자(Converting Constructor)

  • explicit 키워드가 없는 생성자는 묵시적으로 형변환을 허용한다.

  • 변환이 가능한 경우는 해당 인자를 생성자에 인자로 주었을 때 유효한 경우이다.

    #include <iostream>
    #include <cassert>
    
    class Fraction
    {
      int    numerator_;
      int    denominator_;
    
    public:
      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;
    
      doSomething(7);
    }
    
    /* stdout
    Default constructor
    7 / 1
    */
    • 위의 기본 생성자 앞에 explicit 키워드를 붙이면, 묵시적 형변환을 금지시키는 것이 되어 에러가 출력된다.

      error C2664: 'void doSomething(Fraction)': cannot convert argument 1 from 'int' to 'Fraction'

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

C++ this  (0) 2021.03.15
C++ 소멸자 (Destructor)  (0) 2021.03.15
C++ 복사 생성자(Copy Constructor)  (0) 2021.03.12
C++ 위임 생성자 (Delegating Constructor)  (0) 2021.03.12
C++ 멤버 초기화 리스트 (Member Initializer Lists)  (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++/Class 2021. 3. 12. 17:57

복사 생성자(Copy Constructor)

  • 보안이 중요한 경우 복사 생성자를 private로 정의하여 복사를 방지하기도 한다.

  • 기본 복사 생성자는 얕은 복사이므로 주의하자.

    • 이를 방지하기 위해 깊은 복사를 구현해야 하는데, 시간적 여유가 없을 경우 임시로 delete를 통해 구현하기도 한다.
  • copy initialization, direct initialization, uniform initialization 모두 복사 생성자를 호출한다.

    #include <iostream>
    #include <cassert>
    
    class Fraction
    {
      int    numerator_;
      int    denominator_;
    
    public:
      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);
      }
    };
    
    int        main()
    {
      using namespace std;
    
      Fraction frac1(1, 3);
      cout << frac1 << endl;
    
      Fraction frac2 = frac1;   // copy initialization
      cout << frac2 << endl;
    
      Fraction frac3(frac2);    // direct initialization
      cout << frac3 << endl;
    
      Fraction frac4{ frac3 };  // uniform initialization
      cout << frac4 << endl;
    
      Fraction frac5(Fraction(2, 4));
      cout << frac5 << endl;
    }
    
    /* stdout
    Default constructor
    1 / 3
    Copy constructor called
    1 / 3
    Copy constructor called
    1 / 3
    Copy constructor called
    1 / 3
    Default constructor
    2 / 4
    */
    • frac5의 경우 컴파일러가 복사 생성자를 호출하지 않는 식으로 컴파일한다.

반환 값 최적화(RVO, Return Value Optimization)

  • 함수에서 스택에 있는 메모리를 반환하고 그걸 인스턴스에 저장할 때, Release 모드에서는 최적화가 진행된다.

    #include <iostream>
    #include <cassert>
    
    class Fraction
    {
      int    numerator_;
      int    denominator_;
    
    public:
      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);
      }
    };
    
    Fraction doSomething()
    {
      Fraction temp(1, 2);
      std::cout << &temp << std::endl;
      return (temp);
    }
    
    int        main()
    {
      using namespace std;
    
      Fraction result = doSomething();
    
      cout << &result << endl;
      cout << result << endl;
    }
    
    /* stdout(Debug Mode)
    Default constructor
    00CFFD74
    Copy constructor called
    00CFFE60
    1 / 2
    */
    
    /* stdout(Release Mode)
    Default constructor
    008FFA24
    008FFA24
    1 / 2
    */
C++/Class 2021. 3. 12. 17:56

위임 생성자 (Delegating Constructor)

  • C++11

  • 생성자에서 직접 생성하지 않고, 이미 구현된 생성자를 호출해서 대신 생성하도록 하는 것이다.


기본 예제

  • 다음 예제에서는 st2를 생성할 때 id_, name_ 멤버 변수를 초기화 리스트로 초기화하고있다.

    #include <iostream>
    
    using namespace std;
    
    class Student
    {
      int        id_;
      string    name_;
    
    public:
      Student(const string& name_in)
        : id_{0}, name_{name_in}
      {
        print();
      }
    
      Student(const int& id_in, const string& name_in)
        : id_{id_in}, name_{name_in}
      {
        print();
      }
    
      void    print()
      {
        cout << id_ << ' ' << name_ << '\n';
      }
    };
    
    int        main()
    {
      Student st1(0, "jack jack");
      Student st2("Dash");
    }
  • 같은 기능을 하는 코드가 여러 군데에 있으면 좋지 않으므로 다음과 같이 변경하는게 좋다.

    #include <iostream>
    
    using namespace std;
    
    class Student
    {
      int        id_;
      string    name_;
    
    public:
      Student(const string& name_in)
        : Student(0, name_in)
      {}
    
      Student(const int& id_in, const string& name_in)
        : id_{id_in}, name_{name_in}
      {
        print();
      }
    
      void    print()
      {
        cout << id_ << ' ' << name_ << '\n';
      }
    };
    
    int        main()
    {
      Student st1(0, "jack jack");
      Student st2("Dash");
    }
  • C++11 이전에서는 다음과 같이 초기화 함수로 작성하는게 일반적이었다고 하는데, 나는 이 방법이 더 좋은 것 같다.

    #include <iostream>
    
    using namespace std;
    
    class Student
    {
      int        id_;
      string    name_;
    
    public:
      Student(const string& name_in)
      {
        init(0, name_in);
      }
    
      Student(const int& id_in, const string& name_in)
      {
        init(id_in, name_in);
      }
    
      void    init(const int& id_in, const string& name_in)
      {
        id_ = id_in;
        name_ = name_in;
        print();
      }
    
      void    print()
      {
        cout << id_ << ' ' << name_ << '\n';
      }
    };
    
    int        main()
    {
      Student st1(0, "jack jack");
      Student st2("Dash");
    }
C++/Class 2021. 3. 12. 17:53

멤버 초기화 리스트 (Member Initializer Lists)

  • 생성자를 만들 때 멤버들을 초기화해주는 기능

    • C++11부터 배열도 이 방식으로 초기화 가능하다고 한다.
    #include <iostream>
    
    class Something
    {
      int        i_;
      double    d_;
      char    c_;
    
    public:
      Something()
        : i_{ 1 }, d_{ 3.14 }, c_{ 'a' }
      {}
    };
    
    int        main()
    {
      Something a;
    }

기본 값 적용 우선순위가 헷갈릴 때

  #include <iostream>

  class Something
  {
      int        i_ = 100;
      double    d_ = 100.0;
      char    c_ = 'F';

  public:
      Something()
          : i_{ 1 }, d_{ 3.14 }, c_{ 'a' }
      {
          i_ *= 3;
          d_ *= 3.0;
          c_ += 3;
          std::cout << i_ << ' ' << d_ << ' ' << c_;
      }
  };

  int        main()
  {
      Something a;
  }

  /* stdout stderr
  3 9.42 d
  */

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

C++ 복사 생성자(Copy Constructor)  (0) 2021.03.12
C++ 위임 생성자 (Delegating Constructor)  (0) 2021.03.12
C++ 생성자 (Constructor)  (0) 2021.03.12
C++ 접근 지정자 (Access Specifier)  (0) 2021.03.12
C++ Class  (0) 2021.03.12
C++/Class 2021. 3. 12. 17:51

생성자 (Constructor)

  • 인스턴스가 처음 생성될 때 호출되는 함수

    • 멤버 변수부터 생성하고 그 다음 호출된다.

      • 멤버 중에 클래스가 있을 경우 해당 클래스의 생성자부터 호출된다.
      #include <iostream>
      
      class Second
      {
      public:
        Second()
        {
          std::cout << "Second constructor\n";
        }
      };
      
      class First
      {
        Second sec;
      
      public:
        First()
        {
          std::cout << "First constructor\n";
        }
      };
      
      int        main()
      {
        First test;
      }
      
      /* stdout
      Second constructor
      First constructor
      */

  • 생성자를 하나도 만들지 않으면 기본으로 다음과 같은 형태가 만들어진다.

    • 아무 것도 안하는 생성자

    • MyClass::MyClass() {}

    • 복사 생성자(Copy Constructor)


    • MyClass::MyClass(const MyClass& instance_in) { // 내부 값을 모두 복사하는 코드 }

  • 생성자의 파라미터에 기본값을 넣을 수 있다.

  • Uniform initialization과 Copy initialization의 차이

    • 형변환이 안된다. (1장 참고)
  • 기법

    • 생성자를 private으로 일부러 설정하는 기법도 존재한다.
  • 주의사항

    • 생성자의 인자가 하나도 없을 경우에만 소괄호를 생략한다.
      • 기본값 설정으로 인자를 넣지 않아도 될 경우에도 마찬가지이다.
      • #include <iostream> class Fraction { int numerator_; int denominator_; public: Fraction(const int& num = 1, const int& den = 3) { numerator_ = num; denominator_ = den; } void print() { std::cout << numerator_ << '/' << denominator_; } }; int main() { Fraction frac; // frac()으로 하면 안됨 frac.print(); } /* stdout stderr 1/3 */

멤버 초기화 리스트 (Member Initializer Lists)

위임 생성자(Delegating Constructor)

복사 생성자(Copy Constructor)

변환 생성자(Converting Constructor)


참고

C++/Class 2021. 3. 12. 17:47

접근 지정자 (Access Specifier)

  • 종류

    • public : 어디서든 접근 가능하다.

    • private : 해당 클래스와 friend 클래스만 접근 가능하다.

    • protected : private 특성을 기본으로, 상속받은 자식 클래스도 접근 가능하다.

  • 명시하지 않는 경우, 기본 값은 private이다.

  • private이어도 같은 클래스면 다른 인스턴스의 멤버를 사용할 수 있다.

    class Date
    {
      int    year_;
      int    month_;
      int    day_;
    
      void copyFrom(const Date& original)
      {
        year_ = original.year_;
        month_ = original.month_;
        day_ = original.day_;
      }
    };
  • public 멤버 변수들에 대해 uniform initialization이 가능하다.

    #include <iostream>
    
    class Date
    {
    public:
        int    year_;
        int    month_;
        int    day_;
    };
    
    int        main()
    {
        using namespace std;
    
        Date a{ 2021, 3, 1 };
        cout << a.year_ << ' ' << a.month_ << ' ' << a.day_ << '\n';
    }
    
    /* stdout stderr
    2021 3 1
    */

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

C++ 복사 생성자(Copy Constructor)  (0) 2021.03.12
C++ 위임 생성자 (Delegating Constructor)  (0) 2021.03.12
C++ 멤버 초기화 리스트 (Member Initializer Lists)  (0) 2021.03.12
C++ 생성자 (Constructor)  (0) 2021.03.12
C++ Class  (0) 2021.03.12
C++/Class 2021. 3. 12. 17:47

Class

struct vs class

  • c++에서의 구조체도 멤버 함수를 가질 수 있다.

    • _단순한 기능_의 경우 struct를 사용해도 무방하다.
  • class를 사용하는 이유는 다양한 객체지향 프로그래밍 기법들을 적용하기 위함이다.

    • Access Specifier (public, private, protected 등)

접근 지정자 (Access Specifier)

생성자 (Constructor)

소멸자 (Destructor)

this

const

static

friend

익명 객체 (Anonymous Class)

Nested Types

실행 시간 측정 (Run Time Measurement)


참고

C++/TBC++ 2021. 3. 12. 17:46

따라하며 배우는 C++ 8장

Class


참고

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

따라하며 배우는 C++ 10장  (0) 2021.03.19
C++ 따라하며 배우는 C++ 9장  (0) 2021.03.16
따라하며 배우는 C++ 7장  (0) 2021.03.12
따라하며 배우는 C++ 6장  (0) 2021.03.11
따라하며 배우는 C++ 5장  (0) 2021.03.11
C++/Library 2021. 3. 12. 17:33

assert

  • <cassert> 라이브러리

assert

  • Debug 모드에서만 런타임에 작동한다.

    • VS의 전처리기 설정에 매크로가 설정되어있다.

      • Debug 모드에서는 _DEBUG

      • Release 모드에서는 NDEBUG

  • 내부 조건이 거짓이면 Debug Error를 발생시킨다.

      #include <cassert>
    
      int     main()
      {
        assert(false);
      }
  • 최대한 나눠서 쓰는게 디버깅하기에 좋다.

  • static assert

  • 컴파일 타임에 작동한다.

  • Release 모드에서도 작동한다.

  • 에러 문구를 넣어야 한다.

      #include <cassert>
    
      int     main()
    {
        const int x = 5;
        //const int x = 4;  // 컴파일 안됨
    
        static_assert(x == 5, "x should be 5");
      }
  • 릴리즈 모드에선 작동되지 않는다면, 차라리 예외처리를 전부 하는게 맞는 것 아닌가 생각했다.

  • 관련 참고 내용

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

C++ initializer_list  (0) 2021.03.19
C++ chrono  (0) 2021.03.16
C++ vector  (0) 2021.03.12
C++ tuple  (0) 2021.03.12
C++ array  (0) 2021.03.11
C++/Library 2021. 3. 12. 17:31

vector

  • <vector> 라이브러리

기본 예제

  • push_back 함수로 벡터의 맨 뒤에 원소를 추가할 수 있다.

  • foreach로 반복문을 작성할 수 있다. (iterator가 존재하기 때문에 사용 가능)

    #include <iostream>
    #include <vector>
    
    int            main()
    {
      using namespace std;
    
      vector<int> vec;
      for (int i = 0; i < 10; ++i)
        vec.push_back(i);
      for (auto& e : vec)
        cout << e << ' ';
      cout << endl;
    }
    
    /* stdout stderr
    0 1 2 3 4 5 6 7 8 9
    */

멤버 함수

size()capacity()

  • size는 실제 사용하고 있는 공간이고, capacity는 할당받은 전체 공간이다.

    #include <iostream>
    #include <vector>
    
    int     main()
    {
        using namespace std;
    
        vector<int> v{ 1,2 ,3 };
    
        v.resize(2);
    
        for (auto& e : v)
            cout << e << ' ';
        cout << endl;
    
        cout << v.size() << ' ' << v.capacity() << endl;
    
        int* pv = &v[0];
        cout << pv[2] << endl;
    }
    
    /* stdout stderr
    1 2
    2 3
    3
    */

reserve()

  • 공간을 미리 할당받는 것

    #include <iostream>
    #include <vector>
    
    int     main()
    {
      using namespace std;
    
      vector<int> v{ 1,2 ,3 };
    
      v.reserve(100);
    
      for (auto& e : v)
        cout << e << ' ';
      cout << endl;
    
      cout << v.size() << ' ' << v.capacity() << endl;
    }
    
    /* stdout stderr
    1 2 3
    3 100
    */

참고

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

C++ chrono  (0) 2021.03.16
C++ assert  (0) 2021.03.12
C++ tuple  (0) 2021.03.12
C++ array  (0) 2021.03.11
C++ typeinfo  (0) 2021.03.11
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++/Library 2021. 3. 12. 17:25

tuple

  • C++11

  • <tuple> 라이브러리

    #include <iostream>
    #include <tuple>
    
    std::tuple<int, double> getTuple()
    {
      return (std::make_tuple(10, 3.14));
    }
    
    int     main()
    {
      using namespace std;
    
      tuple<int, double>    my_tp = getTuple();
      cout << get<0>(my_tp) << endl;
      cout << get<1>(my_tp) << endl;
    }
    
    /* stdout stderr
    10
    3.14
    */
  • C++17

    • 다음과 같이 작성 가능

      #include <iostream>
      #include <tuple>
      
      std::tuple<int, double> getTuple()
      {
        return (std::make_tuple(10, 3.14));
      }
      
      int     main()
      {
        using namespace std;
      
        auto [a, d] = getTuple();
        cout << a << endl;  // 10
        cout << d << endl;  // 3.14
      }
      
      /* stdout stderr
      10
      3.14
      */

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

C++ assert  (0) 2021.03.12
C++ vector  (0) 2021.03.12
C++ array  (0) 2021.03.11
C++ typeinfo  (0) 2021.03.11
C++ cin.ignore  (0) 2021.03.11
C++/TBC++ 2021. 3. 12. 17:24

따라하며 배우는 C++ 7장

Call by Reference

std::tuple

인라인 함수 (Inline Function)

함수 포인터 (Function Pointer)

std::vector

assert


참고

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

C++ 따라하며 배우는 C++ 9장  (0) 2021.03.16
따라하며 배우는 C++ 8장  (0) 2021.03.12
따라하며 배우는 C++ 6장  (0) 2021.03.11
따라하며 배우는 C++ 5장  (0) 2021.03.11
따라하며 배우는 C++ 4장  (0) 2021.03.10
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++/Library 2021. 3. 11. 16:49

<array>

std::array<T, N>

  • C++11

  • size(), begin(), end(), at() 등의 함수 사용 가능

    • at()으로 접근하는 것은 arr[1] 처럼 직접 주소로 접근하는 것보다 느리지만 조금 더 안전하다.

      • at() 함수는 std::exception을 통해 예외 처리가 되어있다.
  • algorithm 라이브러리의 sort 함수 사용 가능

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

C++ vector  (0) 2021.03.12
C++ tuple  (0) 2021.03.12
C++ typeinfo  (0) 2021.03.11
C++ cin.ignore  (0) 2021.03.11
C++ cin  (0) 2021.03.11
C++/Library 2021. 3. 11. 16:49

데이터 타입 확인

  • <typeinfo> 라이브러리의 typeid().name()을 사용한다.

      cout << typeid(4.0).name() << endl;  // double

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

C++ tuple  (0) 2021.03.12
C++ array  (0) 2021.03.11
C++ cin.ignore  (0) 2021.03.11
C++ cin  (0) 2021.03.11
C++ 난수 생성 (Random Number Generation)  (0) 2021.03.11