Analysis of deep and shallow copy

【Abstract】 The copy construction and assignment copy function of the string class will definitely face shallow copy and deep copy. If you want to use these two functions reasonably, you must find a solution. Shallow copy is faced with the problem of multiple destructuring and changing one will change all, which is easy to cause memory leaks. Next, I will share with you the reasons for shallow copy and the corresponding solutions.

content


1. Briefly analyze copy construction and assignment construction

2. Reference counting

3. Realistic copy method


foreword

Here, let me first talk about under what circumstances is copy construction and under what circumstances is assignment construction.

Copy construction is called when an object is created and initialized with another existing object, and assignment functions can only assign an object to another existing object, so that the existing object has the same value as the source object. condition.


However, if these two functions do not undergo special treatment, they will face an obvious problem, multiple destructuring, this is only one of the problems, and the other problem is that if the content of the address pointed to by one of the pointers is modified, the other It will also be affected, which must not be what you want, so in order to solve these problems, there are the following two solutions.

Shallow copied code (for easy comparison)

class String
{
	String::String(const char* str="")//Constructor, open up a space, and then put a '/0' in it, indicating that it is empty
		:_str(new char[strlen(str)+1])
	{
		strcpy (_str, str);
	}
}

String::String(const String&s)//Construct a copy function, let a pointer point to this memory space
       : _str (s._str)
{}

String &String::operator=(const String&s)// = The overloading of the operator is equivalent to directly letting s1 point to s2, and the pointer's pointer changes.
{
	if(this !=&s)
	{
		_str = s._str;
	}
	return *this;
}

String::~String()//The destructor uses delete[] to destruct a string
{
    if (_str)
    {
        delete [] _p;
    }
}

1. Reference counting method

The reference counting method is actually widely used. In the hash table, the reference counting method is used to limit the occurrence of multiple deletions. Similarly, this usage also occurs in the process management of Linux. In short, this There are many uses for this kind of usage, and I will briefly introduce how to achieve it below.

principle

       In order to deal with the problem of multiple destructors, we add a pcount. Each time a copy constructor is used, it is equivalent to an additional pointer only to this memory space, then the pcount is incremented by 1. Since the function goes out of scope, it will automatically call the destructor to release the memory. Suppose we copy two objects, but in fact we have not opened up space, so there is only one space. Therefore, every time the function wants to call the destructor, we let pcount -1, until pcount is 0, we use delete [] to really release this space, so as to avoid releasing the memory space multiple times and causing memory loss leakage

code show as below

/reference counting copy method
String::String(const char* str = "")//copy
        :_str(new char[strlen(str)+1])//Add a pcount to increase the counting function
        ,_pCount(new int(1))//Open up a block of space and initialize it to 1
{
    strcpy (_str, str);
}

String::String(const String&s)//copy construction
:_str(s._str) //copy construction once, then the reference count is incremented by 1
,_pCount(s._pCount)
{
    (*_pCount)++;
}

String s1("hello world")
{
    String s2(s1);
    s1[2] = 'x';
    cout << s1.c_str() << endl;
    cout << s2.c_str() << endl;
}

String& String:: operator=(const String&s)//Assignment operator overloading
{
    if (this != &s)
    {
        if (--(*_pCount) == 0)//The destructor is not called here,
            //Because destructors are generally called out of the function scope
        {
            delete [] _p;
            delete _pCount;
        }
        _str = s._str;
        _pCount = s._pCount;
        ++(*_pCount);
    }
    return *this;
}

2. Realistic copy

           Realistic copy can also solve the problems caused by shallow copy. Its idea is actually very simple, probably the idea is like this, I wrote it below, if you still don't understand after reading it, you can privately message me

Why does shallow copy cause multiple destruction problems? The fundamental reason is that because there is no open space, several pointers point to the same memory space, so they will affect each other. To solve this problem, you only need to open up the space again, so that they can not interfere with each other. You can do your destructs and changes, and I can manipulate mine.


In this way, s1 points to the memory space of s1, and s2 points to the memory space of s2. When the function goes out of scope, the destructor will be automatically called.

code show as below

String::String(const char* str = "")
:_str(new char[strlen(str)+5])
{
    _str += 4;
    strcpy (_str, str);
    *((int*)(_str-4)) = 1;
}
// s2 (s1)
String::String(const String& s)
:_str(s._str)
{
    *((int*)(_str-4)) += 1;
}
//s1=s2
String& String ::operator=(const String& s)
{
    if (this!=&s)
    {
        if (--(*(int*)(_str - 4)) == 0)
        {
            delete[](_str - 4);
        }
        _str = s._str;
        ++(*(int*)(_str - 4));
    }
    return *this;
}
void String::CopyOnWrite()
{
    if ((*(int*)(_str-4))>1)
    {
        char* newstr = new char[strlen(_str) + 5];
        strcpy(newstr, _str);
        --(*(int*)(_str - 4));
        _str = newstr;
        ++(*(int*)(_str - 4));

    }

}
char &String::operator[](size_t pos)
{
    CopyOnWrite();
    return _str[pos];
}
总结
      深浅拷贝的问题不光会在string类中出现,在C++的链表,二叉树等等数据结构的操作中都会有所体现,因此,不管是什么样的场合下,我们都应该懂得如何去使用这两种方法,毕竟这两种方法各有优点。


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326041425&siteId=291194637