C++的String类的实现 && 深拷贝 && 写时拷贝

浅拷贝的缺陷?

①普通的浅拷贝会在拷贝构造初始化完成后,对象在生命周期结束后,就会析构多次而使程序崩溃。
②如果改变了其中的一个值,另外一个也会随之而改变。
所以为了防止以上两种问题,就有了解决的方案。
这里写图片描述

1. 深拷贝

在拷贝的时候,为其也申请相同大小的一块空间。每个对象都指向自己的空间。这样就不会因为析构同一块空间而出现崩溃。
这里写图片描述
但是,在有些情况下深拷贝会浪费空间。比如:当现实中只需要读取数据并不需要改变数据时,如果采用深拷贝就会再开辟出来一块空间,但是开辟出来的空间的内容和原来空间的内容是相同的。那么也可以不用重新开辟空间,所以就引入了写时拷贝(WriteOnCopy);

2.写时拷贝(writeoncopy)

写时拷贝:即在写的时候才进行开辟空间。
方法一:多定义类时加一个成员变量:引用计数
当类的其他对象也需要这块空间时,就给引用计数加1。在析构时,当引用计数的值为1时,再进行析构,其他情况下例如:对象的生命周期结束时只对引用计数进行减减。

private:
    char* _str;          
    size_t _size;
    size_t _capacity;
    size_t* _pcount;
};

比如下图的描述:
这里写图片描述
方法二:将引用计数开辟在字符串的头部
这里写图片描述
这样的引用计数,在使用时需要在开辟空间时多开辟4个字节,在使用这四个字节时进行强转,然后进行加减的计数操作。
下面是String类的实现以及简单的写时拷贝的实现:

.h文件
#pragma once
#include <iostream>
#include <string.h>
using namespace std;
//空字符串有空间,1个字节的空间
class String
{
public:
    //构造函数
    //处理空字符串的情况
    String()
        :_str(new char[1])
    {
        _str[0] = '\0';
        _size = 0;
        _capacity = 0;
    }
    //字符串非空的情况
    String(const char* str)
        :_str(new char[strlen(str) + 1])
        ,_size(strlen(str))
        ,_capacity(strlen(str))
    {
        strcpy(_str,str);
    }
    const char* c_str()//C语言形式的字符串,返回的是字符串的首指针,遇到\0结束,
                       //所以当创建对象时,如果不传参数,就会崩
    {
        return _str;
    }
    //或者直接一点,把上面的两种情况合二为一
//    String(const char* str = "")
//        :_str(new char[strlen(str) + 1])
//        ,_size_t(strlen(str))
//        ,_capacity(_size)
//        {}

    String(const String& s)
    {
        this->_str = new char[strlen(s._str) + 1];
        this->_size = strlen(s._str);
        this->_capacity = this->_size;
        strcpy(this->_str,s._str);
    }

String& operator=(String& s)
{
    //如果自己给自己赋值,本来的那段空间就会变成随机值
    if(this != &s)
    {
        char* str = new char[strlen(s._str) + 1];//先申请空间,再释放空间,因为放在下面可能会开辟失败,
        delete[] _str;
        _str = str;
        strcpy(_str,s._str );
        _size = s._size ;
        _capacity = s._capacity ;
    }
        return *this;
}

    void Expend(size_t len);
    void PushBack(char ch);
    void PushBack(const char* s);
    void PopBack();
    void PushFront(char ch);
    void PushFront(const char* s);
    void PopFront();
    void Insert(size_t pos,char ch);
    void Insert(size_t pos,const char* s);
    void Erase(size_t pos,size_t len);
    String& Replace(size_t pos,size_t len,const char* s);
    size_t Find(char ch,size_t pos = 0)const;
    size_t Find(const char* s,size_t pos = 0)const;
    String operator+(char ch);
    String operator+=(char ch);
    String operator+(const char* s);
    String& operator+=(const char* s);

    bool operator>(const String& s);
    bool operator>=(const String& s);
    bool operator<(const String& s);
    bool operator<=(const String& s);
    bool operator==(const String& s);
    bool operator!=(const String& s);
    ~String()
    {
        if(_str != NULL)
        {
            _size = 0;
            _capacity = 0;
            delete[] _str;
            _str = NULL;
        }
    }

private:
    char* _str;
    size_t _size;
    size_t _capacity;
};
.cpp文件
#include"String.h"
#include<assert.h>

void String:: Expend(size_t len)
{
    char* str = new char[len + 1];
    strcpy(str,_str);
    delete[] _str;
    _str = str;
    _capacity = len;
}
void String:: PushBack(char ch)
{
    if(this->_size >= this->_capacity)
    {
        Expend(_capacity*2); 
    }
    _str[_size++] = ch;
    _str[_size] = '\0';
}
void String:: PushBack(const char* s)
{
    if((_size + (strlen(s))) >= _capacity)
    {
        Expend(strlen(s)+_size);
    }
    strcpy(_str + _size,s);
    _size += strlen(s);
}
void String:: PopBack()
{
    if(this->_size > 0)
    {
       this->_str[_size - 1] = '\0';
       --this->_size;
    }
}
void String::PushFront(char ch)
{
    if(_size >= _capacity)
    {
        Expend(_capacity * 2);
    }
    int i = _size;
    for(;i >= 0;--i)
    {
        _str[i] = _str[i - 1];
    }
    _str[0] = ch;
    _size += 1;
}
void String::PushFront(const char* s)
{
    int len = strlen(s);
    if(_size > _capacity)
    {
        Expend(_size + len);
    }
    int i = _size;
    for(;i >= 0;--i)
    {
        _str[i + len] = _str[i];
    }
    strncpy(_str,s,len);
    _size += len;
}
void String::PopFront()
{
    if(_size < 1)
    {
        return;
    }
    size_t i = 0;
    for(;i < _size;++i)
    {
        _str[i] = _str[i + 1];
    }
    _size -= 1;
}
void String::Insert(size_t pos,char ch)
{
    assert(pos <=  _size);
    if(_size >= _capacity)
    {
        Expend(_capacity * 2);
    }
    int i = _size;
    for(;i >= (int)pos;--i)
    {
        _str[i] = _str[i - 1];
    }
    _str[pos] = ch;
    _size += 1;
}
void String::Insert(size_t pos,const char* s)
{
    assert(pos <= _size);
    int len = strlen(s);
    if(_size + len >= _capacity)
    {
        Expend(_size + len);
    }
    int i = _size;
    for(;i >= (int)pos;--i)
    {
        _str[i + len] = _str[i];
    }
    strncpy(_str+pos,s,len);
    _size += len;
}
void String::Erase(size_t pos,size_t len)
{
    assert(pos < _size);
    if(pos + len >= _size)
    {
        _str[pos] = '\0';
        _size = pos;
        return;
    }
    int i = pos;
    for(;i <= (int)_size;++i)
    {
        _str[i] = _str[i + len]; 
    }
    _size -= len;
}
size_t String::Find(char ch,size_t pos)const
{
    if(_size < 1)
    {
        return (size_t)-1;
    }
    size_t i = pos;
    for(;i < _size;++i)
    {
        if(_str[i] == ch)
        {
            return i + 1;
        }
    }
    //如果找不到就返回-1(无穷大)
    return (size_t)-1;
}
size_t String::Find(const char* s,size_t pos)const 
{
    assert(s != NULL);
    if(_size < 1)
    {
        return (size_t)-1;
    }
   const char* sub = s;
   const char* src = _str + pos;
   const char* p = _str + pos;
   while(*src)
   {
       src = p;
       sub = s;
       while(*sub && *src == *sub)
       {
           ++sub;
           ++src;
       }
       if(*sub == '\0')
       {
           src -= strlen(s);
           return src - _str;
       }
       else
       {
           ++p;
       }
   }
    return -1;
}
String& String::Replace(size_t pos,size_t len,const char* s)
{
    assert(s != NULL);
    size_t l = strlen(s);
    if(len < l)
    {
        Expend(l - len + _size);
    }
    if(l == len)
    {
        strncpy(_str + pos,s,l);
    }
    else if(l < len)
    {
        strncpy(_str + pos,s,l);
    }
    else
    {
        int i = 0;
        char* src = _str;
        for(i = (int)(_size + len - l);i >= (int)(pos + len) - 1;--i)
        {
            src[i] = src[i - (l - len)];
        }
        _size += (l - len);
        strncpy(_str + pos,s,l);
    }
    return *this;
}
bool String:: operator>(const String& s)
{
    char *s1 = _str;
    char *s2 = s._str;
    while(*s1 && *s2)
    {
        if(*s1 > *s2)
        {
            return true;
        }
        else if(*s1 < *s2)
        {
            return false;
        }
        else
        {
            ++s1;
            ++s2;
        }
    }
    if(*s1)
    {
        return true;
    }
    else
    {
        return false;
    }
}
bool String:: operator==(const String& s)
{
    char *s1 = _str;
    char *s2 = s._str;
    if(_size != s._size)
    {
        return false;
    }
    while(*s1 && *s2)
    {
        if(*s1 != *s2)
        {
            return false;
        }
        ++s1;
        ++s2;
    }
    return true;
}
bool String:: operator>=(const String& s)
{
    if(*this == s ||  *this > s)
    {
        return true;
    }
    return false;
}
bool String:: operator<(const String& s)
{
    if((*this > s)==false && (*this == s)==false)
    {
        return true;
    }
    return false;
}
bool String:: operator<=(const String& s)
{
    if(*this > s)
    {
        return false;
    }
    return true;
}
bool String:: operator!=(const String& s)
{
    if(*this == s)
    {
        return false;
    }
    return true;
}
String String:: operator+(char ch)
{
    String tmp(*this);
    tmp.PushBack(ch);
    return *this;
}
String String:: operator+=(char ch)
{
    this->PushBack(ch);
    return *this;
}
String String::operator+(const char* s)
{
    String tmp(*this);
    tmp.PushBack(s);
    return *this;
}
String& String::operator+=(const char* s)
{
    this->PushBack(s);
    return *this;
}
int main()
{
    String str("hello");
    String str1("helloelac 3244");

    str1.Replace(0,3,"965677");
    cout<<str1.c_str()<<endl;
    //cout<<(str == str1)<<endl;
    //cout<<(str != str1)<<endl;
    //cout<<(str > str1)<<endl;
    //cout<<(str >= str1)<<endl;
    //cout<<(str < str1)<<endl;
    //cout<<(str <= str1)<<endl;
    return 0;
}
以上字符串的相关代码是在Linux的环境下实现的。

下面是写时拷贝的2中不同的方法
方法1:

#pragma once
#include<iostream>
#include<string.h>
using namespace std;
class String
{
public:
    String(const char* s = "")
    :_str(new char[strlen(s) + 1])
    ,_pcount(new size_t[1])
    {
        strcpy(_str,s);
        *_pcount = 1;
    }

    String(const String& s)
  {
      _str = s._str;
      _pcount = s._pcount;
      ++(*_pcount);
  }
    ~String()
    {
        if((*_pcount) == 1)
        {
            delete[] _str;
            _str = NULL;
            _pcount = NULL;
        }
    }
    const char* c_str()
    {
        return _str;
    }
    void CopyOnwrite()
   {
       //如果当前的引用计数就是1,那么就可以直接对该字符串进行读写
       if((*_pcount) == 1)
       {
           return;
       }
       //当前的引用计数不为1,就要重新开辟一段空间,将字符串拷贝过来,减减引用计数的值
       char* tmp = new char[strlen(_str) + 1];
       size_t* p = new size_t[1];
       --(*_pcount);
       strcpy(tmp,_str);
       _str = tmp;
       _pcount = p;
       (*_pcount) = 1;
   }
    String& operator=(const String& s)
  {
      if(&s != this)
      {
          if(--(*_pcount) == 0)
          {
              delete[] _str;
              _pcount = NULL;
          }
          _str = s._str;
          _pcount = s._pcount;
          ++(*_pcount);
      }
      return *this;
  }
    char& operator[](size_t pos)
  {
      CopyOnwrite();
      return _str[pos];
  }
private:
    char* _str;
    size_t* _pcount;
};

//测试代码
#include"WriteCopy.h"


int main()
{
    String s1("hello");
    String s2(s1);
    String s3;
    s3 = s1;
    cout<<s3[0]<<endl;
    cout<<s1.c_str()<<endl;
    cout<<s2.c_str()<<endl;
    cout<<s3.c_str()<<endl;
    return 0;
}

结果为:
这里写图片描述
方法2:

#pragma once
#include<iostream>
#include<string.h>
using namespace std;
//将引用计数放在字符串的头部
class String2
{
public:
    int& GetRefCount()
    {
        return *((int*)(_str - 4));
    }
    String2(const char* s = "")
        :_str(new char[strlen(s) + 5])//多开辟4个字节,是因为要给引用计数留够位置
    {
        _str += 4;
        strcpy(_str,s);
        GetRefCount() = 1;
    }
    ~String2()
    {
        if(--GetRefCount() == 0)
        {
            delete[] (_str - 4);
            _str = NULL;
        }
    }
    const char* c_str()
    {
        return _str;
    }

    String2(const String2& s)
    {
        _str = s._str;
        ++GetRefCount();
    }
    String2& operator=(const String2& s)
    {
        if(s._str != _str)
        {
            if(--GetRefCount() == 0)
            {
                delete[] (_str - 4);
                _str = NULL;
            }
            _str = s._str;
            ++GetRefCount();
        }
        return *this;
    }
    void WriteOnCopy()
    {
        if(GetRefCount() == 1)
        {
            return;
        }
        --GetRefCount();
        char* tmp = new char[strlen(_str) + 5];
        tmp += 4;
        strcpy(tmp,_str);
        _str = tmp;
        GetRefCount() = 1;
    }
    char& operator[](size_t pos)
    {
        WriteOnCopy();
        return _str[pos];
    }

private:
    char* _str;
};
#include"WriteCopy2.h"

int main()
{
    String2 s1("hello");
    String2 s2(s1);
    String2 s3;
    s3 = s1;
    cout<<s1.c_str()<<endl;
    cout<<s2.c_str()<<endl;
    cout<<s3.c_str()<<endl;
    cout<<s1.GetRefCount()<<endl;
    cout<<s2.GetRefCount()<<endl;
    cout<<s3.GetRefCount()<<endl;
    cout<<s3[0]<<endl;
    cout<<s1.GetRefCount()<<endl;
    cout<<s2.GetRefCount()<<endl;
    return 0;
}

这里写图片描述

写时拷贝的这两种方法的实现都是在创建类时定义了一个成员变量,其实还有两个成员变量没有写出来。
如果读者看到不足的地方,欢迎指正。^-^

猜你喜欢

转载自blog.csdn.net/yinghuhu333333/article/details/79921657