More Effective C + +, the implementation of C + + reference counting and copy on write

1. Reference count

c + + reference counting can save memory and reduce the cost of building objects and destructors at the same time. The so-called reference counting simply means that each object shares an entity's data, but we need to realize the record of the object referenced to the data, so that the data can be safely deleted after the end of the last object reference.
Take the string as an example. Suppose we want to copy or assign the string, then the customer we want to present is their own independent string. As follows:

However, for the internal implementation of the computer, this way obviously has the phenomenon of redundant storage, so we expect the internal implementation of the computer to be like this.

In this way, the strings owned by all users are the same, but there will be a problem, that is, when one of the objects is destroyed, the data of other objects will not be accessible. Therefore, we need to count the references of the data, and only the last reference object can really destroy the data.
So the implementation is like this.

2. Copy on write

Although the above implementation counting method realizes reference counting, which can make multiple identical strings share the same data, when any one object modifies the data, the data owned by other objects will also change. Therefore, in order to avoid this situation, copy on write is adopted, That is, when an object wants to modify data, it will re assign a copy for modification, so that the object will have a new copy of data and will not share a copy of data with other objects.

If you are not satisfied with the statement, then it is possible.

String s1 = "Hello";
char *p = &s1[1];
String s2 = s1;
*p = 'x';

The pointer p cannot be recorded by the counter, so this will cause the shared data to be modified.

In order to prevent the above situation, when a string tends to modify the shared data, set the data to the non shareable state, then in the above statement, s2 will not share the data with s1, but copy a new data.

The following is the implementation code with detailed comments:

//Reference count implementation string
class My_string {
public:

    My_string(const char* val = ""):value(new ref_count(val))                            
    {}
    My_string(const My_string& other)                          
    {
    	//If the data can be shared, it can be referenced directly, otherwise it will be reallocated
        if (other.value->isShareable) {
            
            value = other.value;
            ++(value->count);
        }
        else
        {
            value = new ref_count(other.value->data);
        }
        
    }
    
    My_string & operator=(const My_string & other) {
        //Check self assignment
        if (this->value == other.value) {
            return *this;
        }
        //If it is the last reference, delete the data
        if (-- value->count == 0) {
            delete this->value;
        }
        //If you can't share, you need to reallocate space
        if (other.value->isShareable) {
            value = other.value;
            ++value->count;
        }
        else {
            value = new ref_count(other.value->data);
        }
        return *this;
    }

    ~My_string() {
        if ( -- value->count == 0 ) {
            delete value;
        }
    }

    const char& operator[]( int val)  const 
    {
        return value->data[val];
    }

    //Data may be modified, so copy on write strategy is adopted
    char & operator[](int val) {

        if (value->count == 1) {//If there is only one reference at present, there is no need to copy
            return value->data[val];
        }
        --value->count;
        value = new ref_count(value->data);
        value->isShareable = false;//Cannot share in case char * P = &s [i] occurs in this way
        return value->data[val];
    
    }

private:
    //Set as a friend function so that private data can be accessed
    friend ostream& operator<< ( ostream & cout, const My_string& s);
   
    //Encapsulation of data
    struct ref_count {
        //String data
        char* data;
        //The shareable flag is set to false only when writing is possible
        bool isShareable;
        //Counter
        int count;
        //Constructor
        ref_count(const char * data_):count(1),isShareable(true){
            data = new char[strlen(data_) + 1];
            strcpy_s(data , strlen(data_) + 1 , data_);
        }
        //Destructor
        ~ref_count() {
                delete[] data;
        }
    };
    //Each string has a pointer to the encapsulated data
    ref_count * value;
};

// < < operator overloading
ostream& operator<< (ostream& cout ,const My_string & s) {
    for (char* p = s.value->data; *p != '\0'; p++) {
        cout << *p;
    }
    return cout;
}

The above codes refer to Article 29 of More Effective C + +.

Keywords: C++

Added by karnul on Wed, 02 Feb 2022 00:09:56 +0200