深拷贝与浅拷贝深究

深拷贝与浅拷贝。我们可以通过一个故事来了解一下:甲有一本漫画很好看,这时乙也想看。第一种情况,甲将书借给了乙(浅拷贝:资源重复利用了);第二种情况,甲因自己还没看完,没有借给乙,乙就自己重新买了一本(深拷贝:重新弄一个相同的)。

在这里,我会从构造函数,拷贝构造函数、=(赋值)重载、析构函数进行深究。

首先我们通过一个例子来了解一下:

#include<iostream>
using namespace std;
class String
{
  public:
    String(const char *str=""); //构造函数
    String(const String &s);  //拷贝构造函数
    String& operator=(const String &s);  //重载=运算符
    ~String();  //析构函数
}
void main()
{
  String s="Hello";
  String s1=s;
  String s2;   s2
=s1; }

以上的代码是一个简单String类,一般像较为简单的函数,我们可以将函数实现写在类中,这样形成内联函数,执行效率会大大提高。

1.为什么在函数中要对参数进行const限制?

答:在函数中,我们使用const对参数进行限制,为了保证数据的安全性。

2.给形参设置一个默认值(const char  *str="")有什么好处?

答:在构造函数中,我们给形参设定一个空的的默认值。避免在函数体内进行空指针判断,提高了代码的简约性。

3.在拷贝构造函数中,为什么对参数要加引用?

答:为了避免产生拷贝构造后,无休止的拷贝构造。

初级程序员标准如下代码:

class String{
public:
    String(const char *str="")    //构造函数
    {
        m_data=new char[strlen(str)+1];
        strcpy(m_data,str);
    }
    String(const String &s)   //拷贝构造函数
    {
        m_data=new char[strlen(s.m_data)+1];
        strcpy(m_data,s.m_data);
    }
    String& operator=(const String &s)    //重载=
    {
        if(this != &s)
        {
            delete []m_data;
            m_data=new char[strlen(s.m_data)+1];   //问题4:异常安全性原则
            strcpy(m_data,s.m_data);
        } 
        return *this;
    }
    ~String()//析构函数
    {
        delete[] m_data;
        m_data=NULL;
    }
private:
    char *m_data;
};

4.当在重载=运算符时,s1=s2,s1存储数据所需要的空间申请不足时,会导致之前s1对象delete的数据丢失,如何避免这一情况? 这称为“异常安全性原则”。

答:①我们先申请相应的空间,在将复制实例释放空间。 ②我们申请一个局部实例,然后交换临时实例和原来实例。

高级程序员标准如下代码:

  String& operator=(const String &s)    //重载=
    {
        if(this != &s)
        {
            String temp_str(s);
       char *temp_data=temp_str.m_data;
       temp_str.m_data=m_data;
       m_data=temp_data;
} return *this; }

5.当我们用到浅拷贝时,那我们在执行析构函数时,就要注意释放空间的时机。在这里我们通过两种方法过度:①我们引用静态常量(static int count)来记住指向当前数据的实例个数,当count==0时,执行释放空间。

class String
{
public:
    String(const char *str = "")
    {
        m_data = new char[strlen(str)+1];
        strcpy(m_data, str);
        use_count++;
    }

    String(const String &s)
    {
        m_data = s.m_data;
        use_count++;
    }
    //String& operator=(const String &s);

    ~String()
    {
        if(--use_count == 0)
        {
            delete []m_data; 
            m_data = NULL;
        }
    }
private:
    char *m_data;
    static int  use_count;
};

int String::use_count = 0;

void main()
{
    String s("Hello");
    String s1 = s;

    String s2("Linux");
}

注意:当我们调试发现,当重新定义一个不一样的实例时,也会改变use_count的值,这是我们不想看到的。由此当我们执行程序完成,继第二次之后执行构造函数所分配的空间不会被释放,造成内存泄漏。

②我们引用技术类将实例,将String对象的初始化和use_count的增减操作分离。

class String;
ostream& operator<<(ostream &out, const String &s);

class String_rep
{
    friend class String;
    friend ostream& operator<<(ostream &out, const String &s);
public:
    String_rep(const char *str="") : use_count(0)
    {
        m_data = new char[strlen(str)+1];
        strcpy(m_data, str);
    }

    String_rep(const String_rep &rep)
    {
        m_data=new char[strlen(rep.m_data)+1];
        strcpy(m_data,rep.m_data);
    }
    String_rep& operator=(const String_rep &rep)
    {
        if(this!=&rep)
        {
            /*  同问题4:会发生异常
            delete m_data;
            m_data=new char[strlen(rep.m_data)+1];
            strcpy(m_data,rep.m_data)*/
            String_rep temp_rep(rep);
            char *temp_data=temp_rep.m_data;
            temp_rep.m_data=m_data;
            m_data=temp_data;
        }
        return *this;
    }
~String_rep() { delete []m_data; m_data = NULL; } public: void increment() {++use_count;} void decrement() {
  
  if(--use_count==0)
      delete this;
  }
private: char *m_data; int use_count; }; //////////////////////////////////////////////////////// class String { friend ostream& operator<<(ostream &out, const String &s); public: String(const char *str = "") : rep(new String_rep(str)) { rep->increment(); } String(const String &s) { rep = s.rep; rep->increment(); } String& operator=(const String &s); ~String() { rep->decrement(); } public:
  //当完成写实拷贝,如若用户想要对数据进行修改,则需要发生深拷贝,将原类的实例引用常量-1,将新生实例的引用常量+1.
void to_upper()
  {
        rep->decrement();
        rep=new String_rep(rep->m_data);
        rep->increment();

        char *ch = rep->m_data;
        while(*ch++ != '')
            *ch = toupper(*ch);
    }
private: String_rep *rep; }; ostream& operator<<(ostream &out, const String &s) { out<<(s.rep)->m_data; return out; } void main() { String s("Hello"); String s1 = s; cout<<"s = "<<s<<endl; cout<<"s1 = "<<s1<<endl; s.to_upper(); //写实拷贝 cout<<"s = "<<s<<endl; cout<<"s1 = "<<s1<<endl; }

对s进行写实拷贝,执行结果如下:

 

原文地址:https://www.cnblogs.com/single-dont/p/10415838.html