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
    */