C++ Copy Control and Resource Management, Simple Implementation of Intelligent Pointer

C++ Notes on Copy Control and Resource Management, and introduces the concept of some C++ smart pointers, and then implements an intelligent pointer based on reference counting. There will be special research on C++ intelligent pointer.

  • Typically, classes that manage extraclass resources must define copy control members. To define these members, we must first determine the copy semantics of this object. Generally speaking, there are two choices:
    • Make the class behave like a value

      The behavior of a class is like a value: it means that it has its own state. When we copy an object of image value, the copy is completely opposite to the original object. Changing the copy will not have any effect on the original object. Vice versa.
    • Make the behavior of a class look like a pointer

      Classes that behave like pointers share state. When we copy an object of this class, the copy uses the same underlying data as the original object. Changing the copy also changes the original object, and vice versa.
  • Classes of Behavioral Image Values
    • In order to provide the behavior of class values, each object should have its own copy of the resource managed by the class.
    • Class Value Copy Assignment Operator
      • Generally, the operations of destructors and constructors are combined. Similar to destructors, assignment destroys the resources of the left operation object. Similar to constructors, assignment copies data from the right-hand operation object.
      • There are two points to note when writing assignment operators:

        1. If an object is assigned to itself, the assignment operator must work correctly.
        2. Most assignment operators combine the work of destructors and copy constructors
      • When writing an assignment operator, a good way is to copy the right operation object into a local temporary object, and then destroy the left operation object.

  • Classes that define behavior like pointers
    • Reference Counting

      Reference counting works as follows:
      1. In addition to initializing objects, each constructor (except the copy constructor) creates a reference count to record how many objects are sharing state with the created object. When we create an object, there is only one object sharing state, so the counter is initialized to 1.
      2. The copy constructor does not assign a new counter, but copies the data members of a given object, including the counter. The copy constructor increments the shared counter, indicating that the state of a given object is shared by a new user.
      3. Destructor decrement counter, pointing out that there is one less user in the shared state. If the counter becomes zero, the destructor is released.
      4. Copy assignment operator increments the counter of the right operation object and decreases the counter of the left operation object. If the counter of the left operation object becomes zero, it means that its shared state has no users, and the copy assignment operator must destroy the state.
    • Location of reference counts: One method is stored only in dynamic memory. When we create an object, we also assign a counter. When copying or assigning objects, we copy the pointer to the timer. In this way, both the copy and the original object point to the same counter.

  • The implementation of a shared smart pointer based on reference counting is given below.

    #include<iostream>
    using namespace std;
    
    template<class T>
    class SmartPtr{
    public:
        //Constructor
        SmartPtr(T *sp = NULL)
            :m_ptr(sp), use_count(new std::size_t(1)){}
    
        //copy constructor
        SmartPtr(const SmartPtr<T> &ref){
            if (this != &ref){
                m_ptr = ref.m_ptr;
                use_count = ref.use_count;
                ++*use_count;
            }
        }
    
        //copy assignment operator
        SmartPtr& operator=(const SmartPtr<T> &ref){
            if (this == &ref)   //Handling self-assignment
                return *this;
    
            //Determine whether the original use_count is 0
            --*use_count;
            if (*use_count == 0){
                delete m_ptr;
                delete use_count;
                m_ptr = NULL;
                use_count = NULL;
                cout << "operator= delete" << endl;
            }
    
            m_ptr = ref.m_ptr;
            use_count = ref.use_count;
            ++*use_count;
        }
    
        //Destructive function
        ~SmartPtr(){
            --*use_count; 
            if (*use_count == 0){
                delete m_ptr;
                delete use_count;
                m_ptr = NULL;
                use_count = NULL;
                cout << "~SmartPtr()  and delete" << endl;
            }
            else
                cout << "~SmartPtr()  use_count:" << *use_count <<endl;
        }
    
        T * get(){
            return m_ptr;
        }
    
        std::size_t getUse_count(){
            return *use_count;
        }
    private:
        //Basic object pointer
        T *m_ptr;
        std::size_t *use_count;
    };
    
    class Test{
    public:
        Test(int a, char b) :_a(a), _b(b){}
        void print(){
            cout << _a << " " << _b << " ";
        }
    private:
        int _a;
        char _b;
    };
    
    int main(){
        //Built-in data type testing
        cout << "Built-in data type testing" << endl;
        SmartPtr<int> sp1(new int(5));  //Default constructor
        cout << *sp1.get() << "  " << sp1.getUse_count() << endl;
    
        SmartPtr<int> sp2(sp1);  //copy constructor
        cout << *sp2.get() << "  " << sp2.getUse_count() << endl;
    
        SmartPtr<int> sp3;
        sp3 = sp1;          //copy assignment operator
        cout << *sp3.get() << "  " << sp3.getUse_count() << endl;
    
        sp3 = sp3;    //Self-assignment
        cout << *sp3.get() << "  " << sp3.getUse_count() << endl;
    
        //Custom Data Type Testing
        cout << endl << endl << "Custom Data Type Testing" << endl;
        SmartPtr<Test> tp1(new Test(10, 'c'));  //Default constructor
        (tp1.get())->print();
        cout << tp1.getUse_count() << endl;
    
        SmartPtr<Test> tp2(tp1);   //copy constructor
        (tp2.get())->print();
        cout << tp2.getUse_count() << endl;
    
        SmartPtr<Test> tp3;      
        tp3 = tp1;              //copy assignment operator
        (tp3.get())->print();
        cout << tp3.getUse_count() << endl;
    
        tp3 = tp3;   //Self-assignment
        (tp3.get())->print();
        cout << tp3.getUse_count() << endl;
    
        cout << endl << endl <<  "Destructive function" << endl;
        return 0; 
    }
  • The results are as follows:

Keywords: C++ less

Added by parag on Sun, 14 Jul 2019 00:01:58 +0300