• 拷贝构造函数与赋值操作符的具体实现 【转】参考度4.5星


    源地址:http://blog.csdn.net/vincent_lon/article/details/2956766

    PS: 主要看string的定义以及浅拷贝危害

    ------------------------------------------------------------------------

    对含有指针成员变量的类来说,使用默认的拷贝构造赋值操作是不安全的,具体的原因是默认的函数都属于浅拷贝,所谓浅拷贝就是指只对指针本身进行拷贝操作而对于指针所指向的内容不进行任何操作,这显然至少会带来2个问题,第一个是内存泄漏,因为指针本身被一个新值所覆盖,造成指针原先指向的内存将无法得到释放的机会。另一个问题就是重复引用问题,两个(或者更多)的指针指向同一块内存,当对其中一个指针进行delete操作后,所有对其他指向这块已经被释放的内存的指针的操作,结果都是无法预测的。同样,当修改其中一个对象的指针成员所指向内存中的数据时,其它对象会跟着一起被改变,这显然是错误的。

    对于拷贝构造来说,不存在内存泄漏的问题,因为一般对象被创建的时候指针成员都还没有被分配内存,但是存在重复引用的问题。

    所以为了避免这些问题,必须显式的定义这两个函数。

    本文主要讨论这两个函数的实现。。

    1. 拷贝构造函数

    假定有以下类型

    struct CTREE_NODE_TAG

     {
              TYPE                      data;         

              int                            parent;         

              CList<int, int&>     child;
    }

    由于存在CList类型成员child, 所以须实现拷贝构造以及赋值操作符函数;

     CTREE_NODE_TAG(const CTREE_NODE_TAG& rhs)

      :data(rhs.data), parent(rhs.parent)
      {
    //   child.RemoveAll();
       int* pNode;
       POSITION positionRhs = rhs.child.GetHeadPosition();
       POSITION positionTarget = child.GetHeadPosition();
       while(positionRhs!=NULL)
       {
        if(positionTarget == NULL)//只有当目标对象长度小于拷贝对象时才创建新的结点;注:这里可以不需要,应为初始化时child的长度为0
        {
         pNode = new(int);
         *pNode = rhs.child.GetNext(positionRhs);
         child.AddTail(*pNode);
        }
        else//
        {
         *pNode = rhs.child.GetNext(positionRhs);
         child.SetAt(positionTarget, *pNode);
         child.GetNext(positionTarget);
        }

       } 
       while(positionTarget != NULL)//Free the extra element 注:这里可以不需要,应为初始化时child的长度为0
       {
        child.RemoveAt(positionTarget);
        child.GetNext(positionTarget);
       }
      }

    对于非指针类型跟默认实现一样直接复制就可以了,对于指针类型的成员变量,则需要对指针所指向的内容进行复制,而不是简单的对指针本身进行复制;

    注意:  当增加了新的成员变量时,不要忘了更新拷贝构造函数;

    2.    赋值操作符

    对于赋值操作符基本上跟拷贝构造类似,但需要注意以下几点:

    1) 防止内存泄漏: 对于指针型成员变量动态分配内存的)要先释放内存

    2) 对于有基类的情况必须显式的调用基类的的赋值操作;

    class String
    {
      public:
      ~ String(void); // 析构函数
      String(const char* str = NULL);//普通构造函数
      String(const String &other);//拷贝构造函数
      String &operate = (const String &other);//赋值操作
      private:
      char * m_data;   
    }
    // String 的析构函数  
    String::~String(void)   
    {  
      delete [] m_data;  
    }   
    // String 的普通构造函数   
    String::String(const char *str)  
    {  
      if(str==NULL)   
     {  
      m_data = new char[1];   
      *m_data = ‘\0’;   
      }   
     else  
     {  
      int length = strlen(str);   
      m_data = new char[length+1];  
      strcpy(m_data, str);   
     }  
    }   
    // 拷贝构造函数  
    String::String(const String &other)   
    {   
      int length = strlen(other.m_data);  
      m_data = new char[length+1];   
      strcpy(m_data, other.m_data);   
    }  
    // 赋值函数  
    String & String::operate =(const String &other)   
    {   
     // (1) 检查自赋值   
     if(this == &other)  
      return *this;  
     
    // (2) 释放原有的内存资源   
    delete [] m_data;  
     
    // (3)分配新的内存资源,并复制内容
    int length = strlen(other.m_data);  
    m_data = new char[length+1];   
    strcpy(m_data, other.m_data);  
     
    // (4)返回本对象的引用   
    return *this;  
     }
  • 相关阅读:
    关于使用sudo找不到环境变量的问题
    HDFS修改副本数,并生效。
    Linux创建用户,SFTP只允许访问指定目录
    Docker 技术系列之安装Redis单机版和集群版
    Docker 技术系列之安装多版本Mysql5.6和Mysql5.7
    Docker 技术系列之安装Docker Desktop for Mac
    第五章 Mac系统软件-安装Java Web开发环境基本软件
    第四章 感受Mac之美-效率提高从操作快捷键开始
    第三章 感受Mac之美-万里挑一的装机必备软件
    第二章 感受Mac 之美-惊艳从Mac 外设开始,一周后的使用感受
  • 原文地址:https://www.cnblogs.com/nkxyf/p/2598048.html
Copyright © 2020-2023  润新知