【C++】浅拷贝和深拷贝以及怎样实现一个简单的string类!!!

xiaoxiao2021-02-28  12

浅拷贝:

在C++里面很多的时候都会用到用一个对象去给另外一个对象赋值,在表面上看到的两个不同的对象只是两个对象的值是相同的而已,其实在内部两个对象的地址其实是一样的。如下图:

string s1 = "abcdef"; string s2 = s1;

这样的代码存在一个很大的问题,那就是在两个对象指向同一个内存的时候,当有一个对象被销毁的时候,那么这一块内存也会被delete,这样这块内存就会被释放,但是另外一个对象还不知道这块内存已经被释放,这样,当这个对象访问或者对象被销毁的时候就会出现错误。而且这种错误还不是一眼就能看出来的。

深拷贝:

浅拷贝会出现多个对象指向同一块内存的现象,那么可以在重载“=”的时候就给每个对象都分配一块属于自己的内存,这样就可以避免出现多个对象释放同一块内存的现象。那么可以这样实现:

这样的确是可以解决释放内存的问题,但是这样很多次的开辟内存就会使内存碎片变得越来越多,这显然也不是解决这个问题的最佳办法。

解决这个问题可以在这个类里面创建一个计数器,计数器用来计算这个内存有多少个对象在使用,当最后只有一个对象在使用这个内存的时候,当这个对象销毁的时候那么这个内存就可以释放了,如果这个计数器统计的使用这个内存的对象不止一个,那么调用析构函数的时候就不释放这个内存,只需将计数器的数字减一个就可以。

实现计数器的方法:

1、static变量:第一次想到的办法就是用一个static变量来计算,static变量是存储在类里面的公共内存里面,这样是可以实现计数器的功能,但是,在开辟一块新内存的时候这个计数器的值也就会被改变,那么计算出来的值也就不是我们所需要的,这个办法也是不可取的。

2、重新定义一个变量来充当计数器,当每次用一个对象来初始化另外一个对象的时候,可以先将这个指针指向所需的内存,然后将之前的内存释放掉。但是这样的话就会很容易出现内存泄漏的问题,这样做显然也是不可取的。

3、可以和new[]开辟内存的方式一样,在开辟的内存的前面对开辟四个字节的空间来存储计数器的值,这样在释放的时候可以一次就将所开辟的内存一次释放完全。这种方法应该是可以实现的。

模拟实现string:

上面的这种将浅拷贝的深拷贝结合起来的方法也被称为引用计数,最简单的引用计数的实例就是模拟实现string。代码如下:

class String { public: String (char *pstr = "") { if (NULL == pstr) { _pstr = new char[5]; //当pstr为空的时候,只需要将\0拷贝进_pstr里面就好 _pstr += 4; GetNum() = 1; strcpy(_pstr,'\0'); } else { _pstr = new char[strlen(pstr)+1+4]; //这里+1是因为strlen函数计算时候不包括\0,。 _pstr += 4; GetNum() = 1; strcpy(_pstr,pstr); } } int& GetNum() { return *(int*)(_pstr-4); } String (const String &s) :_pstr(s._pstr) { if (this != &s) GetNum()++; } String& operator=(String &s) { if (this != &s) { if (s._pstr&&0 == --s.GetNum()) { delete[] (_pstr-4); } _pstr = s._pstr; GetNum()++; } return *this; } ~String() { if (_pstr&&0 == --GetNum()) //只有在指针不为空,和计数器只有一个对象在使用这块空间的时候将这个内存释放 { delete[] (_pstr-4); } } private: char *_pstr; };

转载请注明原文地址: https://www.6miu.com/read-1150031.html

最新回复(0)