Solutions for C++ Shallow Copy and Shallow Copy

Posted by sgboise on Wed, 17 Jun 2020 21:44:55 +0200

Shallow copy: taking the string class as an example
c++ Shallow Copy and Shallow Copy Solutions When copying a known object, the compilation system automatically calls a constructor, the copy constructor, or the default copy constructor if the user does not define a copy constructor.The default copy construction is a shallow copy, which is equivalent to two pointer variables pointing to the same address space. When the destructor is called, it deletes twice, so the second delete breaks (unreachable addresses).

//shallow copy
class string
    {
    private:
        char* _str;
    public:
        string(char* str = "")//Constructor
        {
            if (nullptr == str)
            {
                str = "";
            }
            _str = new char[strlen(str) + 1];
            strcpy(_str,str);
        }
        string(const string& s)//copy construction
            :_str(s._str)
        {
        }
        string operator =(string& s)//Assignment Construction
        {
            _str = s._str;
            return *this;
        }
        ~string()
        {
            if (_str)
            {
                delete _str;
                _str = nullptr;
            }
        }
    };

Solutions for c++ Shallow Copy and Shallow Copy
Solution 1. Traditional deep copy
Reopening space in copy construction and copying elements from the copied object into a new space

//Disadvantages: need to open up space many times, code redundancy
//Advantage: High readability

class string
{
private:
    char* _str;
public:
    string(char* str = "")
    {
        if (str == nullptr)
        {
            str = "";
        }
        _str = new char[strlen(str) + 1];
        strcpy(_str,str);
    }
    string(const string& s)
        :_str(new char[strlen(s._str)+1])
    {
        strcpy(_str,s._str);
    }
    string& operator=(string& s)
    {
        if (this != &s)
        {
            char* temp = new char[strlen(s._str) + 1];
            strcpy(temp,s._str);
            delete _str;
            _str = temp;
        }
        return *this;
    }
    ~string()
    {
        if (_str)
        {
            delete _str;
            _str = nullptr;
        }
    }
};

Solution 2. Streamline deep copy
1. Address, in which an object is redefined to accept copied object elements, and then the address of the temporary object and the object that needs to be copied are exchanged
2. Pass value, directly exchange addresses of temporary objects and objects that need to be copied

//Advantages: Code efficiency Disadvantage: Low readability

class string
    {
    private:
        char* _str;
    public:
        string(char* str="")
        {
            if (str == nullptr)
            {
                str = "";
            }
            _str = new char[strlen(str) + 1];
            strcpy(_str,str);
        }
        string(const string& s)
            :_str(nullptr)
        {
            string temp(s._str);
            swap(_str,temp._str);
        }
        /*string& operator=(const string& s)
        {
            if (this != &s)
            {
                string temp = s._str;
                swap(_str,temp._str);
            }
            return *this;
        }*/
        string& operator=(string s)//Change direction directly (pass value: temporary variable)
        {
            swap(_str,s._str);
            return *this;
        }
        ~string()
        {
            if (_str)
            {
                delete[] _str;
                _str = nullptr;
            }
        }
    };

Solution 3. Shallow Copy + count (equivalent to last person "closing" when going out, counting people who go in and out)
Count: Define a member_Count, which is initialized directly to 1 in the normal constructor and determines the member of the copied object during copy construction_Count++, which requires a member to copy the object_Whether count is 0 (if 0, copy object member is required_Count++, if >0, copy object member_is requiredCount--), last destructor object_delete is called when the count member is 0

class |string
    {
    private :
        char* _str;
        int* _count;
    public :
        string(char* str="")
            : _count(new int(1))//Call normal construct: _count initialized to 1
            , _str(new char[strlen(str) + 1])
        {
            if (str == nullptr)
            {
                str = "";
            }

            strcpy(_str,str);
        }
        string(const string& s)
            :_str(s._str)
            , _count(s._count)
        {
            ++(*_count);
        }
        string operator=(string& s)
        {
            if (this != &s)
            {
                if (0 == --(*_count))
                {
                    delete[] _str;
                    delete _count;
                    _str = nullptr;
                    _count = nullptr;
                }
                _str = s._str;
                _count = s._count;
                ++(*_count);
            }
            return *this;
        }
        ~string()
        {
            if (_str&&0 == --(*_count))
            {
                delete[] _str;
                delete _count;
                _str = nullptr;
                _count = nullptr;
            }
        }
        char& operator[](size_t index)
        {
            if ((*_count) > 1)
            {
                string temp(_str);
                this->swap(temp);
            }
            return _str[index];
        }
        void swap(string& s)
        {
            std::swap(_str,s._str);
            std::swap(_count,s._count);
        }
        const char& operator[](size_t index)const
        {
            return _str[index];
        }
    };

Topics: C++