对浅拷贝、深拷贝、写时拷贝的简单认识

浅拷贝

也称位拷贝或值拷贝如果对象中管理资源,编译器只是将对象中的值拷贝过来,就会导致多个资源共享一份资源,当一个对象销毁时就会将该资源释放,而这时另一些对象不知道该资源已经被释放,以为还有效,所以,对资源继续操作,就会发生访问违规的情况
比如在模拟实现string类时,构造函数中管理了资源,拷贝构造、赋值运算符重载用浅拷贝的方式会导致资源多次释放,也会导致内存泄漏
我们只是将s1的值放到s2中,这样就会导致s1、s2在底层共用同一块内存空间,当出了作用域要释放时,s3释放成功,当s1释放时,空间已经被s3释放了,s1的指向就成了野指针从而导致资源释放多次出现问题。没有重新开辟空间,对象之前的空间被覆盖了,也会引起内存泄漏。
图1
在这里插入图片描述
要解决同一份资源被多次释放的问题,引入深拷贝

深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。
将其他对象的内容拷贝到另一个对象所在的空间中,而不是直接把值拷贝过来,这样每个对象就有一个自己的资源
图2在这里插入图片描述
下面这个更方便些

//下面这个更方便
  string(const string& s) :_str(nullptr)//如果不初始化为空,那最开始它里面是随机值,不是我们申请的,在释放时就会崩溃
  {
   string strTemp(s._str);//用s里的字符串构造临时对象
   swap(_str, strTemp._str);//内容交换
   //临时对象销毁,这时临时对象的之前内容已经被交换啦
  }
  string& operator=(string s)
  {
   swap(_str, s._str);
   return *this;
  }

写时拷贝

就是在浅拷贝的基础上增加了用计数的方式来实现
引用计数:用来记录资源使用者的个数,在构造时,将资源的计数给成1,没增加一个对象使用该资源,就给计数增加1,当被销毁时就减1,再检查是否需要释放资源;如果减之后对象是0,说明该对象是资源的最后一个使用者,将其释放;否则就不能释放,因为还有其他对象在使用该资源。

namespace bit
{
 class string
 {
 public:
  string(const char* str = "")
  {
   if (str == nullptr)
    str = "";
   _str = new char[strlen(str) + 1];
   strcpy(_str, str);
   _pCount = new int(1);
   //_pCount++;
  }
  string(const string& s) :_str(s._str), _pCount(s._pCount)
  {
   ++(*_pCount);
  }
  //给左操作数减1 释放资源s4
  string& operator=(const string& s)
  {
   if (this != &s)
   {
    release();
    _str = s._str;//共用空间
    _pCount = s._pCount;//共用计数
    ++(*_pCount);
   }
   return *this;
 }
 ~string()
  {
   release();
  }
 private:
  void release()
  {
   if (_str && 0 == --(*_pCount))
   {
    delete[] _str;//指向其他地方,所以把资源释放
    delete _pCount;
    _str = nullptr;
    _pCount = nullptr;
   }
  }
 private:
  char* _str;
  int* _pCount;//按照指针的方式共享
 };
 
}
void TestString()
{
 bit::string s1("hello");
 bit::string s2(s1);
 bit::string s3("world");
 bit::string s4(s3);
 s1 = s3; //s123共用同一份空间   s4
 s4 = s2;
}
int main()
{
 TestString();
 return 0;
} 
发布了73 篇原创文章 · 获赞 2 · 访问量 2850

猜你喜欢

转载自blog.csdn.net/weixin_43219708/article/details/104308197
今日推荐