C++/SmartPointer 2021. 3. 24. 10:44

이동 생성자와 이동 대입 (Move Constructor and Move Assignment)

속도 비교

  • L-value 레퍼런스와 R-value 레퍼런스의 성능 차이가 꽤 존재한다.

    • R-value 레퍼런스의 경우 Deep Copy를 하지 않기 때문이다.

    • 생성자, 소멸자를 호출하는 부분이 강의와 살짝 달랐는데, 강의는 Release 모드여서 Debug 모드에 비해 단계가 줄어있는 상태여서 그랬다.

      • 나도 x86버전의 Release모드로 컴파일했더니 실행 파일이 없다는 LNK1104 에러가 발생하였다.

      • 백신 문제였는데, x64로 컴파일하니 실행에 문제는 없었다. 이유는 모르겠다.

      • 아래는 Debug 모드로 실행했다.

  • L-value 레퍼런스를 사용한 예제

    • Copy Constructor, Copy Assignment를 이용해 값을 Deep Copy한다.

    main.cpp

    #include <iostream>
    #include "Resource.h"
    #include "AutoPtr.h"
    #include "Timer.h"
    
    using namespace std;
    
    AutoPtr<Resource> generateResource()
    {
      AutoPtr<Resource> res(new Resource(10000000));
    
      return res;
    }
    
    int            main()
    {
      Timer timer;
      {
        AutoPtr<Resource> main_res;
        main_res = generateResource();
      }
      timer.elapsed();
    }
    
    /* stdout stderr
    AutoPtr default constructor
    Resource length constructed
    AutoPtr default constructor
    AutoPtr copy constructor
    Resource default constructed
    Resource copy assignment
    AutoPtr destructor
    Resource destoryed
    AutoPtr copy assignment
    Resource default constructed
    Resource copy assignment
    AutoPtr destructor
    Resource destoryed
    AutoPtr destructor
    Resource destoryed
    0.0771911
    */

    Resource.h

    #pragma once
    
    #include <iostream>
    
    class Resource
    {
    public:
      int    *data_ = nullptr;
      unsigned length_ = 0;
    
      Resource()
      {
        std::cout << "Resource default constructed\n";
      }
    
      Resource(unsigned length)
      {
        std::cout << "Resource length constructed\n";
        init(length);
      }
    
      Resource(const Resource& res)
      {
        std::cout << "Resource copy constructed\n";
        init(res.length_);
        for (unsigned i = 0; i < length_; ++i)
          data_[i] = res.data_[i];
      }
    
      ~Resource()
      {
        std::cout << "Resource destoryed\n";
    
        if (data_ != nullptr) delete[] data_;
      }
    
      void    init(unsigned length)
      {
        data_ = new int[length];
        length_ = length;
      }
    
      Resource& operator = (Resource& res)
      {
        std::cout << "Resource copy assignment\n";
        if (&res == this) return *this;
    
        if (data_ != nullptr) delete[] data_;
        init(res.length_);
        for (unsigned i = 0; i < length_; ++i)
          data_[i] = res.data_[i];
        return *this;
      }
    
      void print()
      {
        for (unsigned i = 0; i < length_; ++i)
          std::cout << data_[i] << ' ';
        std::cout << std::endl;
      }
    };

    AutoPtr.h

    #pragma once
    
    #include <iostream>
    
    template<class T>
    class AutoPtr
    {
    public:
      T* ptr_;
    
      AutoPtr(T *ptr = nullptr)
        : ptr_(ptr)
      {
        std::cout << "AutoPtr default constructor\n";
      }
    
      AutoPtr(const AutoPtr& a)
      {
        std::cout << "AutoPtr copy constructor\n";
        ptr_ = new T;
        *ptr_ = *a.ptr_;
      }
    
      ~AutoPtr()
      {
        std::cout << "AutoPtr destructor\n";
        if (ptr_ != nullptr) delete ptr_;
      }
    
      AutoPtr& operator = (const AutoPtr& a)
      {
        std::cout << "AutoPtr copy assignment\n";
        if (&a == this)
          return *this;
    
        if (ptr_ != nullptr) delete ptr_;
    
        ptr_ = new T;
        *ptr_ = *a.ptr_;
        return *this;
      }
    };

    Timer.h

    #pragma once
    
    #include <iostream>
    #include <chrono>
    
    class Timer
    {
        using clock_t = std::chrono::high_resolution_clock;
        using second_t = std::chrono::duration<double, std::ratio<1>>;
    
        std::chrono::time_point<clock_t> start_time = clock_t::now();
    
    public:
        void elapsed()
        {
            std::chrono::time_point<clock_t> end_time = clock_t::now();
    
            std::cout << std::chrono::duration_cast<second_t>(end_time - start_time).count() << std::endl;
        }
    };

  • R-value 레퍼런스를 사용한 예제

    • AutoPtr 클래스에서 Move Constructor, Move Assignment를 이용했다.

    main.cpp

    #include <iostream>
    #include "Resource.h"
    #include "AutoPtr.h"
    #include "Timer.h"
    
    using namespace std;
    
    AutoPtr<Resource> generateResource()
    {
      AutoPtr<Resource> res(new Resource(10000000));
    
      return res;
    }
    
    int            main()
    {
      Timer timer;
      {
        AutoPtr<Resource> main_res;
        main_res = generateResource();
      }
    
      timer.elapsed();
    }
    
    /* stdout stderr
    AutoPtr default constructor
    Resource length constructed
    AutoPtr default constructor
    AutoPtr move constructor
    AutoPtr destructor
    AutoPtr move assignment
    AutoPtr destructor
    AutoPtr destructor
    Resource destoryed
    0.0090543
    */

    Resource.h

    #pragma once
    
    #include <iostream>
    
    class Resource
    {
    public:
      int    *data_ = nullptr;
      unsigned length_ = 0;
    
      Resource()
      {
        std::cout << "Resource default constructed\n";
      }
    
      Resource(unsigned length)
      {
        std::cout << "Resource length constructed\n";
        init(length);
      }
    
      Resource(const Resource& res)
      {
        std::cout << "Resource copy constructed\n";
        init(res.length_);
        for (unsigned i = 0; i < length_; ++i)
          data_[i] = res.data_[i];
      }
    
      ~Resource()
      {
        std::cout << "Resource destoryed\n";
    
        if (data_ != nullptr) delete[] data_;
      }
    
      void    init(unsigned length)
      {
        data_ = new int[length];
        length_ = length;
      }
    
      Resource& operator = (Resource& res)
      {
        std::cout << "Resource copy assignment\n";
        if (&res == this) return *this;
    
        if (data_ != nullptr) delete[] data_;
        init(res.length_);
        for (unsigned i = 0; i < length_; ++i)
          data_[i] = res.data_[i];
        return *this;
      }
    
      void print()
      {
        for (unsigned i = 0; i < length_; ++i)
          std::cout << data_[i] << ' ';
        std::cout << std::endl;
      }
    };

    AutoPtr.h

    #pragma once
    
    #include <iostream>
    
    template<class T>
    class AutoPtr
    {
    public:
      T* ptr_;
    
      AutoPtr(T *ptr = nullptr)
        : ptr_(ptr)
      {
        std::cout << "AutoPtr default constructor\n";
      }
    
      AutoPtr(AutoPtr&& a)
        : ptr_(a.ptr_)
      {
        std::cout << "AutoPtr move constructor\n";
        a.ptr_ = nullptr;
      }
    
      ~AutoPtr()
      {
        std::cout << "AutoPtr destructor\n";
        if (ptr_ != nullptr) delete ptr_;
      }
    
      AutoPtr& operator = (AutoPtr&& a)
      {
        std::cout << "AutoPtr move assignment\n";
        if (&a == this)
          return *this;
    
        if (ptr_ != nullptr) delete ptr_;
    
        ptr_ = a.ptr_;
        a.ptr_ = nullptr;
        return *this;
      }
    };

    Timer.h

    #pragma once
    
    #include <iostream>
    #include <chrono>
    
    class Timer
    {
        using clock_t = std::chrono::high_resolution_clock;
        using second_t = std::chrono::duration<double, std::ratio<1>>;
    
        std::chrono::time_point<clock_t> start_time = clock_t::now();
    
    public:
        void elapsed()
        {
            std::chrono::time_point<clock_t> end_time = clock_t::now();
    
            std::cout << std::chrono::duration_cast<second_t>(end_time - start_time).count() << std::endl;
        }
    };
  • 보다 정확한 시간을 측정하려면 출력 하지 않은 채로 시간을 재면 된다.

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

C++ 순환 의존성 문제 (Circular Dependency Issues)  (0) 2021.03.24
C++ R-value Reference  (0) 2021.03.24
C++ Syntax vs Semantics  (0) 2021.03.24
C++ 스마트 포인터 (Smart Pointer)  (0) 2021.03.22