浅拷贝的缺陷?
①普通的浅拷贝会在拷贝构造初始化完成后,对象在生命周期结束后,就会析构多次而使程序崩溃。
②如果改变了其中的一个值,另外一个也会随之而改变。
所以为了防止以上两种问题,就有了解决的方案。
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;
}
写时拷贝的这两种方法的实现都是在创建类时定义了一个成员变量,其实还有两个成员变量没有写出来。
如果读者看到不足的地方,欢迎指正。^-^