typeScript Wendy 二叉树排序 csv vcpkg ansible seo nodejs视频教程 jq遍历对象 teamviewer验证被拒绝 python零基础教程 python插件 python安装模块 python用什么数据库 java运算符 java课程学习 java怎么配置 java怎么编程 java语言入门 服务器系统下载 真实女友补丁 网络电视软件下载 怪物猎人ol捏脸数据 手机照片恢复免费软件 战地联盟辅助 pr缩放 pdf安装包官方下载 我的世界透视 2700U movavi 光头强换肤助手 pr加速视频 网易云听歌识曲电脑版 SQLite编辑器 无人机数据处理软件 论文封面怎么做 ofd文件阅读器 cad如何旋转图形 下雪软件 kms
当前位置: 首页 > 学习教程  > 编程学习

C++String类与深浅拷贝

2021/1/9 2:04:47 文章标签: 深浅拷贝

CString类与深浅拷贝 OJ常用的String类接口 getline(cin, string) 从标准输入拿走一行给string find / rfind() 返回字串第一次出现的下标 substr(下标) 取子串 c.str() 拿到string类封装的char* ,搭配strcpy使用 浅拷贝 编译器默认合成的拷贝构造和赋值运算…

深浅拷贝">C++String类与深浅拷贝

OJ常用的String类接口

getline(cin, string) 从标准输入拿走一行给string
find / rfind() 返回字串第一次出现的下标
substr(下标) 取子串
c.str() 拿到string类封装的char* ,搭配strcpy使用

浅拷贝

编译器默认合成的拷贝构造和赋值运算符是浅拷贝,意思是从编译器的角度看到什么就原封不动拷贝到当前对象。
导致的问题:多个对象使用同一块空间,释放的空间被其他对象访问会导致程序崩溃。

以下实现深拷贝版本,将对象动态申请的资源也拷贝到当前对象。

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

    String(int len, char c = 0)
        :_pStr(new char[len + 1]) {
        fill(_pStr, _pStr + len, c);
        _pStr[len] = '\0';
    }
    //深拷贝构造
    String(const String& s)
        :_pStr(new char[strlen(s._pStr) + 1])
    {
        strcpy(_pStr, s._pStr);
    }

    String& operator=(String s) {
        swap(_pStr, s._pStr);
        return *this;
    }
    //四步赋值版本
    //String& operator=(const String& s)
    //{
    //  if (this != &s) {
    //      char* pStr = new char[strlen(s._pStr) + 1];
    //      strcpy(pStr, s._pStr);
    //      delete[] _pStr;
    //      _pStr = pStr;
    //  }
    //  return *this;
    //}
    ~String()
    {
        if (_pStr) {
            delete[] _pStr;
        }
    }
    String operator+(const String& s) {
        int len = strlen(s._pStr) + strlen(_pStr) + 1;
        String tmp(len);//借助构造函数
        strcpy(tmp._pStr, _pStr);
        strcat(tmp._pStr, s._pStr);
        return tmp;
    }

    //String operator+(String s) {
    //  int len = size() + s.size() + 1;
    //  char* tmp = new char[len];
    //  strcpy(tmp, _pStr);
    //  strcat(tmp, s._pStr);
    //  swap(tmp, s._pStr);
    //  delete[] tmp;
    //  return s;
    //}
    bool operator==(const String &str)const
    {
        if (strcmp(_pStr, str._pStr) == 0) {
            return true;
        }
        return false;
    }

    size_t size() const
    {
        return strlen(_pStr);
    }

    const char* c_str() const
    {
        return _pStr;
    }
    //取从position所指位置连续取len个字符组成子串返回  
    String& sub_str(int position, int len) {
        if (position<0 || position >= size() || len<0 || len >size()) //参数不合理,不取子串  
        {
        }
        else
        {
            if (position + len - 1 >= size())            //字符串不够长  
                len = size() - position;
            for (int i = 0, j = position; i<len; i++, j++)
                _pStr[i] = _pStr[j];
            _pStr[len] = '\0';
        }
        return *this;
    }
private:
    char* _pStr;
};
/*错误版本*/
    //交换了指针之后会调用析构函数,会释放原先_str指向的空间发生错误
    String(const String& s)
    {
        String strTmp(s._pStr);
        swap(_pStr, strTmp._pStr);
    }

浅拷贝的引用计数版

为了解决浅拷贝的资源管理问题,提出了引用计数,在每个对象里保存关于资源被引用的次数,如果在对象析构时,发现引用计数>0,则不需要释放资源。

具体实现:

思路1:用一个静态成员变量保存计数,让所有该类对象可以访问。错误!当对象没有调用拷贝构造,而是普通构造函数,它的计数和其他引用了资源的对象造成二义性。

思路2:每个对象保存一个计数指针,给每一块空间分配一个计数,拷贝构造时复制指针,析构时–自己指针的引用计数。可行!

思路3:在字符串空间前预先分配四字节用来计数,以下基于此实现!

class String {
public:

    String(const char* str = "")
    {
        if (NULL == str) {
            str = "";
        }
        _pStr = new char[strlen(str) + 1 + 4];
        _pStr += 4;
        get_ref_count() = 1;
        strcpy(_pStr, str);
    }

    String(const String& s)
        :_pStr(s._pStr)
    {
        ++get_ref_count();
    }
    String& operator=(const String& s) {
        if (this != &s) {
            _Release();
            _pStr = s._pStr;
            ++get_ref_count();
        }
        return *this;
    }
    void _Release()
    {
        --get_ref_count();
        if (get_ref_count() == 0 && _pStr) {
            delete[] (_pStr-4);
        }
    }
    ~String()
    {
        if (_pStr) {
            _Release();
        }
    }
    int& get_ref_count()
    {
        return *(int*)(_pStr - 4);
    }
    //返回值修改内容,写时拷贝
    char& operator[](size_t index)
    {
        //有两个以上对象引用了这块空间,不能修改其他对象的值
        if (get_ref_count() > 1) {
            --get_ref_count();
            String strTmp(_pStr);
            _pStr = NULL;
            swap(_pStr, strTmp._pStr);
        }
        return _pStr[index];
    }
    //[]做右值
    const char& operator[](size_t index)const
    {
        return _pStr[index];
    }
private:
    char* _pStr;
};

由于引用计数出现,必须要保证对计数和资源的操作必须是原子的,存在竟态条件。所以在多线程下这个类还不完善。

测试用例:

String s("abc");
String s2(s);
String s3("lll");
s3 = s;
s3[0] = 'w';//写时拷贝
String s4("444");
String s5(s4);
s5 = s;

本文链接: http://www.dtmao.cc/news_show_1100280.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?